Development Tip

JavaScript에 사전 구현이 있습니까?

yourdevel 2021. 1. 10. 19:40
반응형

JavaScript에 사전 구현이 있습니까?


JavaScript에서 인덱서로 배열을 구현하려면 어떻게해야합니까? .Net에 사전과 같은 것이 있습니까?


기술적으로는 아니지만 사전과 같은 일반 JavaScript 객체를 사용할 수 있습니다.

var a = {"a":"wohoo", 2:"hello2", "d":"hello"};
alert(a["a"]);
alert(a[2]);
alert(a["d"]);

John Resig (jQuery의 저자) 는 최근 자바 스크립트의 사전 조회에 대해 게시했습니다 .

그의 해결책은 사전 값을 객체의 속성으로 할당하는 것입니다. 위 기사에서 그대로 붙여 넣은 코드 :

// The dictionary lookup object
var dict = {};
// Do a jQuery Ajax request for the text dictionary
$.get( "dict/dict.txt", function( txt ) {
  // Get an array of all the words
  var words = txt.split( "\n" );

  // And add them as properties to the dictionary lookup
  // This will allow for fast lookups later
  for ( var i = 0; i < words.length; i++ ) {
    dict[ words[i] ] = true;
  }

  // The game would start after the dictionary was loaded
  // startGame();
});

// Takes in an array of letters and finds the longest
// possible word at the front of the letters
function findWord( letters ) {
  // Clone the array for manipulation
  var curLetters = letters.slice( 0 ), word = "";

  // Make sure the word is at least 3 letters long
  while ( curLetters.length > 2 ) {
    // Get a word out of the existing letters
    word = curLetters.join("");

    // And see if it's in the dictionary
    if ( dict[ word ] ) {
      // If it is, return that word
      return word;
    }

    // Otherwise remove another letter from the end
    curLetters.pop();
  }
}

javascript 데이터 구조 라이브러리 인 buckets 을 사용해 볼 수 있으며 사전에있는 모든 유형의 객체를 사용할 수 있습니다.


지난 프로젝트에서 저는 수만 행의 데이터를 읽은 다음 그리드 및 차트에 표시하기 위해 데이터를 그룹화하고 집계하는 브라우저 클라이언트 애플리케이션을 만드는 작업을 맡았습니다. 대상 기술은 HTML 5, CSS 3 및 EMCS 5 (2013 년 6 월 최신 브라우저)였습니다. 이전 브라우저 호환성이 문제가되지 않았기 때문에 외부 라이브러리는 D3 (JQuery 없음)으로 제한되었습니다.

데이터 모델을 구축해야했습니다. 이전에 C #으로 빌드했고 사용자 지정 사전 개체에 의존하여 데이터, 그룹 및 집계에 빠르게 액세스했습니다. 몇 년 동안 JavaScript로 일하지 않았기 때문에 사전을 찾기 시작했습니다. JavaScript에는 여전히 진정한 기본 사전이 없습니다. 몇 가지 샘플 구현을 찾았지만 실제로 내 기대에 부합하는 것은 없습니다. 그래서 하나를 만들었습니다.

제가 언급했듯이 저는 몇 년 동안 JavaScript로 일하지 않았습니다. 발전 (또는 정보의 웹 가용성)은 매우 인상적이었습니다. 이전의 모든 작업은 클래스 기반 언어로 이루어 졌으므로 프로토 타입 기본 언어에 익숙해지는 데 시간이 좀 걸렸습니다 (아직 갈 길이 멀었습니다).

대부분과 마찬가지로이 프로젝트는 시작하기 전에 마감되었으므로 클래스 기반에서 프로토 타입 기반 언어로 전환 할 때 예상되는 많은 새로운 실수를 저지르면서 배웠습니다. 생성 된 사전은 기능적이지만 시간이지나면서 덜 새로운 것으로 만들어서 개선 할 수있는 몇 가지 개선 사항을 깨달았습니다. 사전을 재 작업하기 전에 프로젝트 자금이 부족했습니다. 아, 그리고 내 위치는 동시에 자금을 잃었습니다 (어떻게 일어날 수 있는지 놀랍습니다). 그래서 배운 것을 사용하여 사전을 다시 만들고 사전이 실제로 배열보다 성능이 향상되었는지 확인하기로 결정했습니다.

/*
* Dictionary Factory Object
* Holds common object functions. similar to V-Table
* this.New() used to create new dictionary objects
* Uses Object.defineProperties so won't work on older browsers.
* Browser Compatibility (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties)
*      Firefox (Gecko) 4.0 (2), Chrome 5, IE 9, Opera 11.60, Safari 5
*/
function Dict() {

    /*
    * Create a new Dictionary
    */
    this.New = function () {
        return new dict();
    };

    /*
    * Return argument f if it is a function otherwise return undefined
    */
    function ensureF(f) {
        if (isFunct(f)) {
            return f;
        }
    }

    function isFunct(f) {
        return (typeof f == "function");
    }

    /*
    * Add a "_" as first character just to be sure valid property name
    */
    function makeKey(k) {
        return "_" + k;
    };

    /*
    * Key Value Pair object - held in array
    */
    function newkvp(key, value) {
        return {
            key: key,
            value: value,
            toString: function () { return this.key; },
            valueOf: function () { return this.key; }
        };
    };

    /*
    * Return the current set of keys. 
    */
    function keys(a) {
        // remove the leading "-" character from the keys
        return a.map(function (e) { return e.key.substr(1); });
        // Alternative: Requires Opera 12 vs. 11.60
        // -- Must pass the internal object instead of the array
        // -- Still need to remove the leading "-" to return user key values
        //    Object.keys(o).map(function (e) { return e.key.substr(1); });
    };

    /*
    * Return the current set of values. 
    */
    function values(a) {
        return a.map(function(e) { return e.value; } );
    };

    /*
    * Return the current set of key value pairs. 
    */
    function kvPs(a) {
        // remove the leading "-" character from the keys
        return a.map(function (e) { return newkvp(e.key.substr(1), e.value); });
    }

    /*
    * Returns true if key exists in the dictionary.
    * k - Key to check (with the leading "_" character) 
    */
    function exists(k, o) {
        return o.hasOwnProperty(k);
    }

    /*
    * Array Map implementation
    */
    function map(a, f) {
        if (!isFunct(f)) { return; }
        return a.map(function (e, i) { return f(e.value, i); });
    }

    /*
    * Array Every implementation
    */
    function every(a, f) {
        if (!isFunct(f)) { return; }
        return a.every(function (e, i) { return f(e.value, i) });
    }

    /*
    * Returns subset of "values" where function "f" returns true for the "value"
    */
    function filter(a, f) {
        if (!isFunct(f)) {return; }
        var ret = a.filter(function (e, i) { return f(e.value, i); });
        // if anything returned by array.filter, then get the "values" from the key value pairs
        if (ret && ret.length > 0) {
            ret = values(ret);
        }
        return ret;
    }

    /*
    * Array Reverse implementation
    */
    function reverse(a, o) {
        a.reverse();
        reindex(a, o, 0);
    }

    /**
    * Randomize array element order in-place.
    * Using Fisher-Yates shuffle algorithm.
    * (Added just because:-)
    */
    function shuffle(a, o) {
        var j, t;
        for (var i = a.length - 1; i > 0; i--) {
            j = Math.floor(Math.random() * (i + 1));
            t = a[i];
            a[i] = a[j];
            a[j] = t;
        }
        reindex(a, o, 0);
        return a;
    }
    /*
    * Array Some implementation
    */
    function some(a, f) {
        if (!isFunct(f)) { return; }
        return a.some(function (e, i) { return f(e.value, i) });
    }

    /*
    * Sort the dictionary. Sorts the array and reindexes the object.
    * a - dictionary array
    * o - dictionary object
    * sf - dictionary default sort function (can be undefined)
    * f - sort method sort function argument (can be undefined)
    */
    function sort(a, o, sf, f) {
        var sf1 = f || sf; // sort function  method used if not undefined
        // if there is a customer sort function, use it
        if (isFunct(sf1)) {
            a.sort(function (e1, e2) { return sf1(e1.value, e2.value); });
        }
        else {
            // sort by key values
            a.sort();
        }
        // reindex - adds O(n) to perf
        reindex(a, o, 0);
        // return sorted values (not entire array)
        // adds O(n) to perf
        return values(a);
    };

    /*
    * forEach iteration of "values"
    *   uses "for" loop to allow exiting iteration when function returns true 
    */
    function forEach(a, f) {
        if (!isFunct(f)) { return; }
        // use for loop to allow exiting early and not iterating all items
        for(var i = 0; i < a.length; i++) {
            if (f(a[i].value, i)) { break; }
        }
    };

    /*
    * forEachR iteration of "values" in reverse order
    *   uses "for" loop to allow exiting iteration when function returns true 
    */
    function forEachR(a, f) {
        if (!isFunct(f)) { return; }
        // use for loop to allow exiting early and not iterating all items
        for (var i = a.length - 1; i > -1; i--) {
            if (f(a[i].value, i)) { break; }
        }
    }

    /*
    * Add a new Key Value Pair, or update the value of an existing key value pair
    */
    function add(key, value, a, o, resort, sf) {
        var k = makeKey(key);
        // Update value if key exists
        if (exists(k, o)) {
            a[o[k]].value = value;
        }
        else {
            // Add a new Key value Pair
            var kvp = newkvp(k, value);
            o[kvp.key] = a.length;
            a.push(kvp);
        }
        // resort if requested
        if (resort) { sort(a, o, sf); }
    };

    /*
    * Removes an existing key value pair and returns the "value" If the key does not exists, returns undefined
    */
    function remove(key, a, o) {
        var k = makeKey(key);
        // return undefined if the key does not exist
        if (!exists(k, o)) { return; }
        // get the array index
        var i = o[k];
        // get the key value pair
        var ret = a[i];
        // remove the array element
        a.splice(i, 1);
        // remove the object property
        delete o[k];
        // reindex the object properties from the remove element to end of the array
        reindex(a, o, i);
        // return the removed value
        return ret.value;
    };

    /*
    * Returns true if key exists in the dictionary.
    * k - Key to check (without the leading "_" character) 
    */
    function keyExists(k, o) {
        return exists(makeKey(k), o);
    };

    /*
    * Returns value assocated with "key". Returns undefined if key not found
    */
    function item(key, a, o) {
        var k = makeKey(key);
        if (exists(k, o)) {
            return a[o[k]].value;
        }
    }

    /*
    * changes index values held by object properties to match the array index location
    * Called after sorting or removing
    */
    function reindex(a, o, i){
        for (var j = i; j < a.length; j++) {
            o[a[j].key] = j;
        }
    }

    /*
    * The "real dictionary"
    */
    function dict() {
        var _a = [];
        var _o = {};
        var _sortF;

        Object.defineProperties(this, {
            "length": { get: function () { return _a.length; }, enumerable: true },
            "keys": { get: function() { return keys(_a); }, enumerable: true },
            "values": { get: function() { return values(_a); }, enumerable: true },
            "keyValuePairs": { get: function() { return kvPs(_a); }, enumerable: true},
            "sortFunction": { get: function() { return _sortF; }, set: function(funct) { _sortF = ensureF(funct); }, enumerable: true }
        });

        // Array Methods - Only modification to not pass the actual array to the callback function
        this.map = function(funct) { return map(_a, funct); };
        this.every = function(funct) { return every(_a, funct); };
        this.filter = function(funct) { return filter(_a, funct); };
        this.reverse = function() { reverse(_a, _o); };
        this.shuffle = function () { return shuffle(_a, _o); };
        this.some = function(funct) { return some(_a, funct); };
        this.sort = function(funct) { return sort(_a, _o, _sortF, funct); };

        // Array Methods - Modified aborts when funct returns true.
        this.forEach = function (funct) { forEach(_a, funct) };

        // forEach in reverse order
        this.forEachRev = function (funct) { forEachR(_a, funct) };

        // Dictionary Methods
        this.addOrUpdate = function(key, value, resort) { return add(key, value, _a, _o, resort, _sortF); };
        this.remove = function(key) { return remove(key, _a, _o); };
        this.exists = function(key) { return keyExists(key, _o); };
        this.item = function(key) { return item(key, _a, _o); };
        this.get = function (index) { if (index > -1 && index < _a.length) { return _a[index].value; } } ,
        this.clear = function() { _a = []; _o = {}; };

        return this;
    }


    return this;
}

클래스와 프로토 타입 객체를 정신적으로 조화 시키려고 시도하면서 얻은 깨달음 중 하나는 프로토 타입이 기본적으로 생성 된 객체에 대한 v- 테이블이라는 것입니다. 또한 인클로저의 기능은 V- 테이블 항목처럼 작동 할 수도 있습니다. 프로젝트가 진행됨에 따라 최상위 개체에 개체 유형에 대한 공통 함수가 포함되어 있고 솔루션에 사용되는 실제 개체를 만드는 데 사용되는 "this.New (args)"메서드가 포함 된 개체 팩토리를 사용하기 시작했습니다. 이것이 제가 사전에 사용한 스타일입니다.

사전의 핵심은 Array, Object 및 KeyValuePair 객체입니다. "addOrUpdate"메소드는 키와 값을 취하며 다음을 수행합니다.

  1. KeyValuePair를 생성합니다.
  2. 키를 속성 이름으로 사용하고 배열 길이를 속성 값으로 사용하여 객체에 새 속성을 추가합니다.
  3. 배열에 KeyValuePair를 추가하여 객체의 새 속성 값을 배열의 인덱스로 만듭니다.

참고 : 개체 속성 이름은 "거의 모든"유니 코드 문자로 시작할 수 있음을 읽었습니다. 이 프로젝트는 "모든"유니 코드 문자로 시작할 수있는 고객 데이터를 처리합니다. 유효하지 않은 속성 이름으로 인해 사전이 파열되지 않도록하기 위해 키 앞에 밑줄 (_)을 붙이고 사전 외부에 키를 반환 할 때 해당 밑줄을 제거합니다.

사전이 작동하려면 내부 배열과 객체가 동기화 상태로 유지되어야합니다. 이를 보장하기 위해 배열이나 객체가 외부에 노출되지 않습니다. "If"테스트에 등호가 하나만 있고 왼쪽에 실수로 값이있을 때 발생할 수있는 변경과 같은 우발적 인 변경을 피하고 싶었습니다.

If(dict.KeyObj[“SomeKey”] = “oops”) { alert(“good luck tracing this down:-)”); }

딕셔너리의 이러한 일반적인 오류는 버그 (증상)가 계산, 표시 등에서 나타나기 시작할 때 추적하기 매우 어려울 수 있습니다. 따라서 "this"속성은 어느 쪽에도 액세스 할 수 없습니다. 이 보호주의는 내가 프로토 타입을 더 많이 파지 않은 이유 중 하나입니다. Array 및 Object가 노출 된 내부 개체를 사용하고 "call"또는 "apply"메서드를 사용할 때 해당 내부 개체를 전달하는 것이 마음에 들었습니다. 아직 확실하지 않기 때문에 나중에 볼 수 있습니다. 핵심 배열 및 개체를 보호하려는 목적을 무력화하는 내부 개체를 노출해야합니다.

내가 만든 첫 번째 사전 객체로 만든 newb 실수 중 일부를 수정했습니다.

  • "Dict ()"함수는 각 사전 객체에 대한 대부분의 작업 코드를 포함합니다. 포함 된 함수를 사용해야하는지 여부와 실제 사전 개체의 기능을 결정하는 데 사용한 기준 :
    • 한 줄 이상의 코드
    • 다른 포함 된 함수에서 사용
    • 버그 / 문제를 발견함에 따라 성장으로 인해 변경 될 수 있습니다.
  • 의미가있는 곳에 사용 된 Array 메서드 및 속성 이름. C #에서 나는 "forEach"대신 "length"대신 "Count"를 사용하거나 "ForEach"대신에 "ForEach"를 사용하여 사전을 덜 사용하도록 만드는 작업을 수행했습니다. 배열 이름을 사용하면 이제 대부분의 경우 사전을 배열로 사용할 수 있습니다. 불행히도 대괄호 접근자를 만드는 방법을 찾지 못했습니다 (예 : val = dict [key]). 어쨌든 좋은 방법 일 수 있습니다. 그것에 대해 생각할 때 나는 val = dict [12]와 같은 것이 제대로 작동하는지 확신하기가 어려웠습니다. 숫자 12는 키로 쉽게 사용할 수 있었기 때문에 그러한 호출의 "의도"를 알 수있는 좋은 방법을 생각할 수 없었습니다.
  • 밑줄 접두사 처리를 완전히 묶었습니다. 작업중인 프로젝트에서 다양한 데이터 모델 개체에서이를 확산하고 반복했습니다. 못 생겼어!

JS에서 { "index": anyValue}는 단지 사전입니다. JSON (http://www.json.org/)의 정의를 참조 할 수도 있습니다.


Javascript에서 .Net 사전에 사용한 가장 가까운 구현은 해시 객체입니다 (링크 : http://www.mojavelinux.com/articles/javascript_hashes.html 참조 ). 내부적으로 배열을 구현하고 .Net 사전과 유사한 이름의 메서드를 가지고 있습니다.


ECMAScript 6 (일명 2015 JavaScript 사양) 은 Map이라는 사전 인터페이스를 지정합니다 . 모든 유형의 임의의 키를 지원하고, 읽기 전용 size속성을 가지며, 객체와 같은 프로토 타입 관련 항목으로 복잡하지 않으며, 새 for...of...구성 또는을 사용하여 반복 할 수 있습니다 Map.forEach. MDN에 대한 문서는 여기 에서 확인하고 브라우저 호환성 표는 여기에서 확인하십시오 .


다른 사람들이 쓰는대로 개체를 사용합니다. 문자열 이외의 것을 키로 저장하는 경우 jsonize하십시오. 자바 스크립트에서 다양한 사전 구현의 성능 고려 사항은 블로그 게시물참조하세요 .


var nDictionary = Object.create(null);

function setDictionary(index, value) {
    nDictionary[index] = value;
}

function getDictionary(index) {
    return nDictionary[index];
}

setDictionary(81403, "test 1");
setDictionary(81404, "test 2");
setDictionary(81405, "test 3");
setDictionary(81406, "test 4");
setDictionary(81407, "test 5");

alert(getDictionary(81403));

이 구현이 실행 중입니다. 키 값 쌍을 처음 추가하면 일종의 키 유형이 안전합니다. 잘 작동하며 Map과 독립적입니다.

Git (항상 업데이트 됨)

function Dictionary() {

  this.dictionary = [];  
  this.validateKey = function(key){
    if(typeof key == 'undefined' || key == null){
        return false;
    }
    if(this.dictionary.length){
        if (!this.hasOwnProperty(this.dictionary[0], "key")) {
            return false;
        }
        if(typeof this.dictionary[0].key != typeof key){
            return false;
        }
    }
    return true;
  };
  this.hasOwnProperty = function (obj, prop) {
    var proto = obj.__proto__ || obj.constructor.prototype;
    return (prop in obj) &&
        (!(prop in proto) || proto[prop] !== obj[prop]);
    };
}



Dictionary.prototype = {

   Add: function(key, value) {
     if(!this.validateKey(key)){       
            return false;
     }
     if(!this.ContainsKey(key)){
      this.dictionary.push({ key: key, value: value });
      return true;
     }
     return false;
   },
   Any: function() {
     return this.dictionary.length > 0;
   },
   ContainsKey: function(key) {
     if(!this.validateKey(key)){       
            return false;
     }
      for (var i = 0; i < this.dictionary.length; i++) {
         var keyValuePair = this.dictionary[i];
         if (typeof keyValuePair != "undefined" && keyValuePair != null) {
            if (this.hasOwnProperty(keyValuePair, "key")) {
               if (keyValuePair.key == key) {
                  return true;
               }
            }
         }
      }
      return false;
   },
   ContainsValue: function(value) {
      for (var i = 0; i < this.dictionary.length; i++) {
         var keyValuePair = this.dictionary[i];
         if(typeof keyValuePair != "undefined" && keyValuePair != null){
                if (this.hasOwnProperty(keyValuePair, "value")) {
              if(value == null && keyValuePair.value == null){
                return true;
              }
              if ((value != null && keyValuePair.value == null) ||
                    (value == null && keyValuePair.value != null)) {
                  continue;
              }
              // compare objects content over json.
              if(JSON.stringify(value) === JSON.stringify(keyValuePair.value)){
                return true;
              }
            }
         }
      }
      return false;
   },
   Count: function() {
     return this.dictionary.length;
   },
   GetValue: function(key){
     if(!this.validateKey(key)){       
            return null;
     }
        for (var i = 0; i < this.dictionary.length; i++) {
         var keyValuePair = this.dictionary[i];
         if (typeof keyValuePair != "undefined" && keyValuePair != null) {
            if (this.hasOwnProperty(keyValuePair, "key")) {
               if (keyValuePair.key == key) {
                  return keyValuePair.value;
               }
            }
         }
      }
      return null;
   },
   Keys: function(){
    var keys = [];
    for (var i = 0; i < this.dictionary.length; i++) {
      var keyValuePair = this.dictionary[i];
      if (typeof keyValuePair != "undefined" && keyValuePair != null) {
        if (this.hasOwnProperty(keyValuePair, "key")) {
          keys.push(keyValuePair.key);
        }
      }
    }
     return keys;
   },
   Remove: function(key){
    if(!this.validateKey(key)){       
            return;
    }
    for (var i = 0; i < this.dictionary.length; i++) {
         var keyValuePair = this.dictionary[i];
         if (typeof keyValuePair != "undefined" && keyValuePair != null) {
            if (this.hasOwnProperty(keyValuePair, "key")) {
               if (keyValuePair.key == key) {
                  this.dictionary.splice(i, 1);
                  return;                  
               }
            }
         }
      }
   },
   Values: function(){
    var values = [];
    for (var i = 0; i < this.dictionary.length; i++) {
      var keyValuePair = this.dictionary[i];
      if (typeof keyValuePair != "undefined" && keyValuePair != null) {
        if (this.hasOwnProperty(keyValuePair, "value")) {
          values.push(keyValuePair.value);
        }
      }
    }
     return values;
   },
};

사용 방법은 다음과 같습니다.

var dic = new Dictionary();

var success = dic.Add("test", 5);
success = dic.Add("test1", 4);
success = dic.Add("test2", 8);
success = dic.Add(3, 8);
var containsKey = dic.ContainsKey("test2");
containsKey = dic.ContainsKey(3);

var containsValue = dic.ContainsValue(8);

var value = dic.GetValue("test1");

var keys = dic.Keys();
var values = dic.Values();

dic.Remove("test1");

var keys = dic.Keys();
var values = dic.Values();

참조 URL : https://stackoverflow.com/questions/5594850/is-there-a-dictionary-implementation-in-javascript

반응형