web-dev-qa-db-ja.com

JavaScriptに辞書実装はありますか?

JavaScriptでインデクサーを使用して配列を実装するにはどうすればよいですか? .Netに辞書のようなものはありますか?

54
pencilCake

技術的にはそうではありませんが、辞書のような通常のJavaScriptオブジェクトを使用できます。

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

John Resig(jQueryの作成者) 最近投稿 JavaScriptでの辞書検索について。

彼の解決策は、辞書の値をオブジェクトのプロパティとして割り当てることです。上記の記事から逐語的に貼り付けたコード:

// 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();
  }
}
10
Andy

buckets を試すことができます。これはJavaScriptデータ構造ライブラリであり、辞書内の任意のタイプのオブジェクトを使用できます。

9
Daniel

前回のプロジェクトでは、数万行のデータを読み取り、グリッドに表示してグラフ化するためにデータをグループ化および集約するブラウザークライアントアプリケーションを作成する仕事をしました。ターゲットテクノロジーは、HTML 5、CSS 3、EMCS 5(2013年6月の最新ブラウザー)です。古いブラウザの互換性は問題ではないため、外部ライブラリはD3(JQueryなし)に制限されていました。

データモデルを作成する必要がありました。以前にC#で作成し、カスタムディクショナリオブジェクトに依存して、データ、グループ、および集計にすばやくアクセスしました。私は長年JavaScriptで働いていなかったので、辞書を探し始めました。 JavaScriptにはまだネイティブ辞書がありませんでした。いくつかのサンプル実装を見つけましたが、本当に期待に応えるものはありませんでした。だから私はそれを作りました。

私が言ったように、私は長年JavaScriptで働いていませんでした。進歩(またはWebでの情報の入手可能性)は非常に印象的でした。私の以前の仕事はすべてクラスベースの言語でしたので、プロトタイプのベース言語は慣れるのに時間がかかりました(そして、私はまだ長い道のりがあります)。

このプロジェクトは、ほとんどの場合と同様、開始前に期限が切れていたため、クラスベースからプロトタイプベースの言語に移行するときに予想される多くのnewbミスを犯しながら、学びました。作成された辞書は機能的でしたが、しばらくして、初心者を少なくすることで改善できることに気付きました。辞書を書き直す時間がある前に、プロジェクトは資金を使い果たしました。ああ、そして私の立場は同時に資金を失いました(それがどのように起こるか驚くべきことです)。そこで、私が学んだことを使用して辞書を再作成し、辞書が実際に配列よりもパフォーマンスが向上したかどうかを判断することにしました。

/*
* 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;
}

クラスとプロトタイプオブジェクトを精神的に調整しようとしたときに私が抱えていたエピファニーの1つは、プロトタイプが基本的に作成されたオブジェクトのvテーブルであることです。さらに、エンクロージャ内の関数は、vテーブルエントリのように動作することもできます。プロジェクトの進行に伴い、オブジェクトファクトリーの使用を開始しました。最上位レベルのオブジェクトにはオブジェクトタイプの共通関数が含まれ、ソリューションで使用される実際のオブジェクトの作成に使用される「this.New(args)」メソッドが含まれました。これは辞書に使用したスタイルです。

辞書の中核は、Array、Object、およびKeyValuePairオブジェクトです。 「addOrUpdate」メソッドはキーと値を受け取り、次のことを行います。

  1. KeyValuePairを作成します
  2. プロパティ名としてキーを使用し、プロパティ値として配列の長さを使用して、オブジェクトに新しいプロパティを追加します
  3. KeyValuePairを配列に追加し、オブジェクトの新しいプロパティ値を配列のインデックスにします

注:「ほぼすべての」Unicode文字で始まるオブジェクトプロパティ名を読みました。このプロジェクトでは、「任意の」Unicode文字で始まる顧客データを処理します。無効なプロパティ名のために辞書が爆発しないように、キーの前にアンダースコア(_)を付け、辞書の外部にキーを返すときにそのアンダースコアを取り除きます。

辞書を機能させるには、内部配列とオブジェクトの同期を保つ必要があります。これを保証するために、配列もオブジェクトも外部に公開されません。 「If」テストの等号が1つだけで、左側の値が誤って設定されている場合に発生する可能性があるような偶発的な変更を避けたいと思いました。

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

ディクショナリに関するこの典型的なエラーは、バグ(症状)が計算や表示などに現れ始めると、追跡するのが非常に困難になる可能性があります。したがって、「this」プロパティはどちらにもアクセスできません。この保護主義は、私がプロトタイプを詳しく調べなかった理由の1つです。 ArrayとObjectを公開した状態で内部オブジェクトを使用し、「call」または「apply」メソッドを使用するときにその内部オブジェクトを渡すことは頭を悩ませていました。コアの配列とオブジェクトを保護する目的を無効にする内部オブジェクトを公開する必要があります。

最初に作成した辞書オブジェクトで行ったnewbの間違いのいくつかを修正しました。

  • 「Dict()」関数には、各辞書オブジェクトのほとんどの作業コードが含まれています。囲まれた関数を使用するかどうかを決定するために使用した基準と実際の辞書オブジェクトの機能:
    • 複数行のコード
    • 他の囲まれた関数によって使用されます
    • バグ/問題を発見すると、成長の原因となる可能性があります
  • 意味のある配列メソッドとプロパティ名を使用しました。 C#から来て、「長さ」の代わりに「カウント」または「forEach」の代わりに「ForEach」を使用することで、辞書を使いにくくしました。配列名を使用することにより、ほとんどの場合、辞書を配列として使用できるようになりました。残念ながら、ブラケットアクセサ(例:val = dict [key])を作成する方法を見つけることができませんでしたが、とにかく良いことかもしれません。それについて考えるとき、val = dict [12]のようなものが正しく動作することを確認するのが困難でした。 12という数字は簡単にキーとして使用できたので、そのような呼び出しの「意図」を知る良い方法は考えられませんでした。
  • アンダースコアのプレフィックス処理を完全に囲みました。私が働いていたプロジェクトでは、これをさまざまなデータモデルオブジェクトに広げて繰り返しました。 wasかった!
7
Dan Ricker

JSでは、{"index":anyValue}は単なる辞書です。 JSONの定義(http://www.json.org/)も参照できます

6
Allen Hsu

Javascriptで.Net辞書に使用した最も近い実装は、ハッシュオブジェクトです(リンク: http://www.mojavelinux.com/articles/javascript_hashes.html を参照)。フードの下に配列を実装し、.Net辞書のメソッドと同様の名前のメソッドを持っています。

2
CarneyCode

ECMAScript 6(別名2015 JavaScript仕様)、 Mapという名前の辞書インターフェイスを指定 。任意のタイプの任意のキーをサポートし、読み取り専用のsizeプロパティを持ち、オブジェクトなどのプロトタイプ関連のもので散らかっておらず、新しいfor...of...コンストラクトまたはMap.forEachを使用して反復処理できます。 MDN here およびブラウザ互換性テーブル here のドキュメントを確認してください。

2
Hugo Wood

他の人が書くようにオブジェクトを使用します。キーとして文字列以外のものを保存している場合は、それらをjsonizeしてください。これを参照してください ブログ投稿 javascriptでのさまざまな辞書実装のパフォーマンスに関する考察。

1
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));
0
Jason Williams

この実装を実行しています。キーと値のペアを最初に追加すると、キータイプが安全になります。それはうまく動作し、マップから独立しています:

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();
0
Franki1986