JavaScriptで連想配列/ハッシュを行う方法


574

JavaScriptを使用して、C#で行うような方法で統計を保存する必要があります。

Dictionary<string, int> statistics;

statistics["Foo"] = 10;
statistics["Goo"] = statistics["Goo"] + 1;
statistics.Add("Zoo", 1);

JavaScriptのHashtableようなものはありDictionary<TKey, TValue>ますか?
どうすればこのような方法で値を格納できますか?


1
jsは緩やかに型付けされているため、文字列またはintを宣言するだけの方法はなく、varを宣言して文字列またはintに割り当てるだけです。:D
ゴードングスタフソン

xDictをチェックしてみてください。jsfiddle.net/very/MuVwdこれは、Javascriptで書かれた辞書String =>すべてのものです。
ロバート

この記事には、Javascriptで内部
Shuklaswag

承認された回答は2009年に書かれたもので、文字列キーのみをサポートしています。文字列以外のキーについては、Vitaliiの回答のように、MapまたはWeakMapを使用します
ToolmakerSteve

回答:


564

JavaScriptオブジェクトを連想配列として使用します

連想配列:単純な言葉では、連想配列はインデックスとして整数値の代わりに文字列を使用します。

でオブジェクトを作成する

var dictionary = {};

JavaScriptでは、次の構文を使用してオブジェクトにプロパティを追加できます。

Object.yourProperty = value;

同じための代替構文は次のとおりです。

Object["yourProperty"] = value;

次の構文を使用してオブジェクトマップのキーを作成することもできる場合

var point = { x:3, y:2 };

point["x"] // returns 3
point.y // returns 2

次のようにfor..inループ構造を使用して、連想配列を反復処理できます。

for(var key in Object.keys(dict)){
  var value = dict[key];
  /* use key/value for intended purpose */
}

36
「連想配列」を初期化する作者のアプローチnew Array()は不利なことに注意してください。記事は、最終的にはその欠点を言及し、示唆new Object()または{}優先の選択肢として、それは終わり近くだと、私はほとんどの読者はそこまでを取得することはできません恐れています。
ダニエル・ルバロフ

24
不合格。JavaScriptはオブジェクト参照をキーとしてサポートしていませんが、Flash / AS3辞書のようなものはサポートしています。JavaScriptではvar obj1 = {}; var obj2 = {}; var table= {}; table[obj1] = "A"; table[obj2] = "B"; alert(table[obj1]); //displays B、キーobj1とobj2を区別できないため、どちらも文字列に変換され、「オブジェクト」のようなものになります。完全に失敗し、参照および循環参照を使用したタイプセーフなシリアライゼーションが、JavaScriptでそのまままたは困難または非実行になります。Flash / AS3では簡単です。
Triynko

まあ、我々は平等をチェックするか定義することによって検証することができますJSでの唯一の方法は等しい:このような何か方法を Point.prototype.equals = function(obj) { return (obj instanceof Point) && (obj.x === this.x) && (obj.y === this.y); };
Nadeem

1
@Leoにconsole.log({A 'B'、C 'D'} [fooは])あなたのA、Bを与える必要
ychaouche

2
@Leo例は間違っているようです。for... in辞書はそのキーをループするので、そこにObject.keysは見当違いのようです。Object.keysディクショナリのキーの配列を返します。配列の場合、その「キー」をfor... inループし ます。これは、配列の場合、値ではなくインデックスです。
JHH

434
var associativeArray = {};
associativeArray["one"] = "First";
associativeArray["two"] = "Second";
associativeArray["three"] = "Third";

オブジェクト指向言語を使用している場合は、この記事を確認してください


38
これをより少ない行で行うこともできます。var associativeArray = {"one": "First"、 "two": "second"、 "three": "Third"}; 次に、associativeArray ["one"]は「First」を返し、assocativeArray ["four"]はnullを返します。
トニーウィッカム

2
申し訳ありませんが、@ JuusoOhtonen、私は6年前に投稿しました(時間の経過は信じられないほどです)。リンクを更新しました。確認して、疑問がないかどうかお気軽に質問してください
ダニ・クリコ

145

最近のすべてのブラウザは、javascript Mapオブジェクトをサポートしています。ObjectよりMapを使用する理由はいくつかあります。

  • オブジェクトにはプロトタイプがあるため、マップにはデフォルトのキーがあります。
  • オブジェクトのキーは文字列であり、マップの任意の値にすることができます。
  • オブジェクトのサイズを追跡する必要があるときに、マップのサイズを簡単に取得できます。

例:

var myMap = new Map();

var keyObj = {},
    keyFunc = function () {},
    keyString = "a string";

myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, "value associated with keyObj");
myMap.set(keyFunc, "value associated with keyFunc");

myMap.size; // 3

myMap.get(keyString);    // "value associated with 'a string'"
myMap.get(keyObj);       // "value associated with keyObj"
myMap.get(keyFunc);      // "value associated with keyFunc"

他のオブジェクトから参照されていないキーをガベージコレクションする場合は、マップの代わりにWeakMapを使用することを検討してください


5
うまくいけば、数年後にはこれが最も多く投票されることになるでしょう。
キャメロンリー

1
@CameronLeeきっとそうなります–LoïcFaure
-Lacroix、

1
これMapは、キーがオブジェクトであるが、参照ではなく値で比較する必要がある場合にはほとんど役に立ちません。
Siyuan Ren

7
この回答が書かれてから1年以上が経過しましたが、「すべての最新のブラウザがマップをサポートしている」とはまだ正しくありません。デスクトップでのみ、少なくとも基本的なマップのサポートを期待できます。モバイルデバイスではありません。たとえば、Androidブラウザはマップをまったくサポートしていません。デスクトップ上でも、一部の実装は不完全です。たとえば、IE11は「for ... of ...」を介した列挙をまだサポートしていないため、IEの互換性が必要な場合は、嫌な.forEach kludgeを使用する必要があります。また、JSON.stringify()は、私が試したどのブラウザーのMapでも機能しません。また、初期化子はIEまたはSafariでは機能しません。
Dave Burton

3
優れたブラウザサポートがあります。再び確かめる。いずれにせよ、これは簡単にポリフィルできるため、ネイティブブラウザーのサポートは問題ではありません。
ブラッド

132

特別な理由がない限り、通常のオブジェクトを使用してください。JavaScriptのオブジェクトプロパティは、ハッシュテーブルスタイルの構文を使用して参照できます。

var hashtable = {};
hashtable.foo = "bar";
hashtable['bar'] = "foo";

これでfoo、とbar要素の両方を次のように参照できます。

hashtable['foo'];
hashtable['bar'];
// or
hashtable.foo;
hashtable.bar;

もちろん、これはキーが文字列でなければならないことを意味します。それらが文字列でない場合、それらは内部的に文字列に変換されるので、YMMVでも機能する可能性があります。


1
整数としてのキーは問題ありませんでした。stackoverflow.com/questions/2380019/...
ジョナスElfström

10
ジョナス:プロパティの設定時に整数が文字列に変換されることにvar hash = {}; hash[1] = "foo"; alert(hash["1"]);注意してください:警告 "foo"。
Tim Down

17
キーの1つが「プロト」または「」の場合はどうなりますか?
PleaseStand

5
JavaScript ではオブジェクトをキーとして使用できないことに注意してください。そうですが、それらは文字列表現に変換されるため、どのオブジェクトもまったく同じキーになります。以下の@TimDownのjshashtableの提案を参照してください。
ericsoco 2013

21
この例では、2つのインスタンスでfooとbarをキーと値の両方として使用しているため、混乱を招きます。var dict = {}; dict.key1 = "val1"; dict["key2"] = "val2";dictのkey1要素がとの両方dict["key1"]で同等に参照できることを示すと、より明確になりdict.key1ます。
ジム

49

JSのすべてのオブジェクトはハッシュテーブルのように動作し、通常はハッシュテーブルとして実装されているので、私はそれでうまくいきます...

var hashSweetHashTable = {};

26
「ハッシュテーブル」の値に実際にアクセスする方法を示していないため、反対票を投じました。
IQAndreas 2015年

私は9年遅れています(当時、このサイトは言うまでもなく、プログラミングについてはほとんど何も知りませんでした)。しかし、マップにポイントを格納しようとしていて、何かが既に存在するかどうかを確認する必要がある場合はどうでしょうか。地図上のポイントで?その場合は、HashTableを使用して座標(文字列ではなくオブジェクト)で検索するのが最適です。
マイクウォーレン

@MikeWarren if (hashSweetHashTable.foo)は、ifブロックfooが設定されている場合に入力する必要があります。
Koray Tugay

21

したがって、C#ではコードは次のようになります。

Dictionary<string,int> dictionary = new Dictionary<string,int>();
dictionary.add("sample1", 1);
dictionary.add("sample2", 2);

または

var dictionary = new Dictionary<string, int> {
    {"sample1", 1},
    {"sample2", 2}
};

JavaScriptで

var dictionary = {
    "sample1": 1,
    "sample2": 2
}

C#の辞書オブジェクトのような便利なメソッドが含まdictionary.ContainsKey() JavaScriptで私たちが使用できるhasOwnPropertyようにします

if (dictionary.hasOwnProperty("sample1"))
    console.log("sample1 key found and its value is"+ dictionary["sample1"]);

1
私についての賛成票を提出する必要はありませんhasOwnProperty
brichins

18

キーを文字列だけでなく任意のオブジェクトにする必要がある場合は、私のjshashtableを使用できます。


3
オブジェクトがJS-style-Object-as-associative-arraysのキーとして実際に使用できないという事実に気づくまでに何時間も費やしましたか?ティム、ありがとう。
ericsoco 2013

1
Flash / AS3辞書は、他のほとんどの言語とともに、オブジェクト参照をキーとしてサポートしています。JavaScriptはまだそれを実装していませんが、将来的にはある種のMapクラスとして仕様に組み込まれると思います。ここでも、ポリフィルを使用します。標準についてはこれだけです。ああ、ちょっと待ってください... 2015年にようやく、Mapが到着したようです:stackoverflow.com/a/30088129/88409、「モダン」ブラウザでサポートされていますlol:kangax.github.io/compat-table/es6/#マップ(実際には広くサポートされていません)。AS3からわずか10年。
Triynko

ティム、おそらく、可能であればMap()を使用するようにjshashtableを更新する必要があります。
Dave Burton

1
@DaveBurton:良い計画。時間があればすぐにそうします。
Tim Down

6
function HashTable() {
    this.length = 0;
    this.items = new Array();
    for (var i = 0; i < arguments.length; i += 2) {
        if (typeof (arguments[i + 1]) != 'undefined') {
            this.items[arguments[i]] = arguments[i + 1];
            this.length++;
        }
    }

    this.removeItem = function (in_key) {
        var tmp_previous;
        if (typeof (this.items[in_key]) != 'undefined') {
            this.length--;
            var tmp_previous = this.items[in_key];
            delete this.items[in_key];
        }

        return tmp_previous;
    }

    this.getItem = function (in_key) {
        return this.items[in_key];
    }

    this.setItem = function (in_key, in_value) {
        var tmp_previous;
        if (typeof (in_value) != 'undefined') {
            if (typeof (this.items[in_key]) == 'undefined') {
                this.length++;
            } else {
                tmp_previous = this.items[in_key];
            }

            this.items[in_key] = in_value;
        }

        return tmp_previous;
    }

    this.hasItem = function (in_key) {
        return typeof (this.items[in_key]) != 'undefined';
    }

    this.clear = function () {
        for (var i in this.items) {
            delete this.items[i];
        }

        this.length = 0;
    }
}

1
これに反対票を投じている人のために、なぜコメントしていただけますか?この回答は2011年に投稿されたもので、現在の日付ではありません。
Birey

2
私は反対票を投じなかったが...オブジェクトとして配列を使うべきではない。これがあなたの意図であったかどうかは100%わかりません。インデックスを再作成するには、削除しない配列でスライスを使用します。削除は問題ありませんが、未定義に設定されます-明示的にする方が良いです。使用=未定義のオブジェクトでもb / cの方が高速です(ただし、メモリが多くなります)。つまり、常に{}配列ではなくオブジェクトを使用します。[]またはnew Array()、文字列キーを使用する場合は、jsエンジンに問題があります。1つの変数に対して2つの型が表示され、最適化が行われないか、配列で実行されて実現されます。オブジェクトに変更する必要があります(再割り当ての可能性があります)。
Graeme Wicksted 2015

2
Alex Hawkinsの回答と同様に、このかなり複雑に見えるコードが実際に有用であり、ここに記載されている他の短い回答よりも優れている理由を説明してください。
Thomas Tempelmann、2016

6

これを作成して、オブジェクトキーのマッピング、(forEach()メソッドを使用した)列挙の機能、クリアなどの問題を解決しました。

function Hashtable() {
    this._map = new Map();
    this._indexes = new Map();
    this._keys = [];
    this._values = [];
    this.put = function(key, value) {
        var newKey = !this.containsKey(key);
        this._map.set(key, value);
        if (newKey) {
            this._indexes.set(key, this.length);
            this._keys.push(key);
            this._values.push(value);
        }
    };
    this.remove = function(key) {
        if (!this.containsKey(key))
            return;
        this._map.delete(key);
        var index = this._indexes.get(key);
        this._indexes.delete(key);
        this._keys.splice(index, 1);
        this._values.splice(index, 1);
    };
    this.indexOfKey = function(key) {
        return this._indexes.get(key);
    };
    this.indexOfValue = function(value) {
        return this._values.indexOf(value) != -1;
    };
    this.get = function(key) {
        return this._map.get(key);
    };
    this.entryAt = function(index) {
        var item = {};
        Object.defineProperty(item, "key", {
            value: this.keys[index],
            writable: false
        });
        Object.defineProperty(item, "value", {
            value: this.values[index],
            writable: false
        });
        return item;
    };
    this.clear = function() {
        var length = this.length;
        for (var i = 0; i < length; i++) {
            var key = this.keys[i];
            this._map.delete(key);
            this._indexes.delete(key);
        }
        this._keys.splice(0, length);
    };
    this.containsKey = function(key) {
        return this._map.has(key);
    };
    this.containsValue = function(value) {
        return this._values.indexOf(value) != -1;
    };
    this.forEach = function(iterator) {
        for (var i = 0; i < this.length; i++)
            iterator(this.keys[i], this.values[i], i);
    };
    Object.defineProperty(this, "length", {
        get: function() {
            return this._keys.length;
        }
    });
    Object.defineProperty(this, "keys", {
        get: function() {
            return this._keys;
        }
    });
    Object.defineProperty(this, "values", {
        get: function() {
            return this._values;
        }
    });
    Object.defineProperty(this, "entries", {
        get: function() {
            var entries = new Array(this.length);
            for (var i = 0; i < entries.length; i++)
                entries[i] = this.entryAt(i);
            return entries;
        }
    });
}


クラスのドキュメント Hashtable

方法:

  • get(key)
    指定されたキーに関連付けられた値を返します。
    パラメータ::
    key値を取得するキー。

  • put(key, value)
    指定された値を指定されたキーに関連付けます。
    パラメータ::
    key値を関連付けるキー。
    value:キーに関連付ける値。

  • remove(key)
    指定されたキーとその値を削除します。
    パラメータ::
    key削除するキー。

  • clear()
    すべてのハッシュテーブルをクリアし、キーと値の両方を削除します。

  • indexOfKey(key)
    追加順序に基づいて、指定されたキーのインデックスを返します。
    パラメータ::
    keyインデックスを取得するキー。

  • indexOfValue(value)
    追加順序に基づいて、指定された値のインデックスを返します。
    パラメータ::
    valueインデックスを取得する値。
    注:
    この情報はindexOf()配列のメソッドによって取得されるため、メソッドとオブジェクトを比較しますtoString()

  • entryAt(index)
    指定されたインデックスのエントリを表す、キーと値の2つのプロパティを持つオブジェクトを返します。
    パラメータ::
    index取得するエントリのインデックス。

  • containsKey(key)
    ハッシュテーブルに指定されたキーが含まれているかどうかを返します。
    パラメータ::
    keyチェックするキー。

  • containsValue(value)
    ハッシュテーブルに指定された値が含まれているかどうかを返します。
    パラメータ::
    valueチェックする値。

  • forEach(iterator)
    指定されたのすべてのエントリを反復しますiterator
    パラメータ:
    value::3つのパラメータを持つ方法keyvalue及びindexindexエントリのインデックスを表します。

    プロパティ:

  • length (読み取り専用
    ハッシュテーブルのエントリ数を取得します。

  • keys (読み取り専用
    ハッシュテーブル内のすべてのキーの配列を取得します。

  • values (読み取り専用
    ハッシュテーブル内のすべての値の配列を取得します。

  • entries (読み取り専用
    ハッシュテーブル内のすべてのエントリの配列を取得します。メソッドの同じ形式で表されますentryAt()


2

https://gist.github.com/alexhawkins/f6329420f40e5cafa0a4

var HashTable = function() {
  this._storage = [];
  this._count = 0;
  this._limit = 8;
}


HashTable.prototype.insert = function(key, value) {
  //create an index for our storage location by passing it through our hashing function
  var index = this.hashFunc(key, this._limit);
  //retrieve the bucket at this particular index in our storage, if one exists
  //[[ [k,v], [k,v], [k,v] ] , [ [k,v], [k,v] ]  [ [k,v] ] ]
  var bucket = this._storage[index]
    //does a bucket exist or do we get undefined when trying to retrieve said index?
  if (!bucket) {
    //create the bucket
    var bucket = [];
    //insert the bucket into our hashTable
    this._storage[index] = bucket;
  }

  var override = false;
  //now iterate through our bucket to see if there are any conflicting
  //key value pairs within our bucket. If there are any, override them.
  for (var i = 0; i < bucket.length; i++) {
    var tuple = bucket[i];
    if (tuple[0] === key) {
      //overide value stored at this key
      tuple[1] = value;
      override = true;
    }
  }

  if (!override) {
    //create a new tuple in our bucket
    //note that this could either be the new empty bucket we created above
    //or a bucket with other tupules with keys that are different than 
    //the key of the tuple we are inserting. These tupules are in the same
    //bucket because their keys all equate to the same numeric index when
    //passing through our hash function.
    bucket.push([key, value]);
    this._count++
      //now that we've added our new key/val pair to our storage
      //let's check to see if we need to resize our storage
      if (this._count > this._limit * 0.75) {
        this.resize(this._limit * 2);
      }
  }
  return this;
};


HashTable.prototype.remove = function(key) {
  var index = this.hashFunc(key, this._limit);
  var bucket = this._storage[index];
  if (!bucket) {
    return null;
  }
  //iterate over the bucket
  for (var i = 0; i < bucket.length; i++) {
    var tuple = bucket[i];
    //check to see if key is inside bucket
    if (tuple[0] === key) {
      //if it is, get rid of this tuple
      bucket.splice(i, 1);
      this._count--;
      if (this._count < this._limit * 0.25) {
        this._resize(this._limit / 2);
      }
      return tuple[1];
    }
  }
};



HashTable.prototype.retrieve = function(key) {
  var index = this.hashFunc(key, this._limit);
  var bucket = this._storage[index];

  if (!bucket) {
    return null;
  }

  for (var i = 0; i < bucket.length; i++) {
    var tuple = bucket[i];
    if (tuple[0] === key) {
      return tuple[1];
    }
  }

  return null;
};


HashTable.prototype.hashFunc = function(str, max) {
  var hash = 0;
  for (var i = 0; i < str.length; i++) {
    var letter = str[i];
    hash = (hash << 5) + letter.charCodeAt(0);
    hash = (hash & hash) % max;
  }
  return hash;
};


HashTable.prototype.resize = function(newLimit) {
  var oldStorage = this._storage;

  this._limit = newLimit;
  this._count = 0;
  this._storage = [];

  oldStorage.forEach(function(bucket) {
    if (!bucket) {
      return;
    }
    for (var i = 0; i < bucket.length; i++) {
      var tuple = bucket[i];
      this.insert(tuple[0], tuple[1]);
    }
  }.bind(this));
};


HashTable.prototype.retrieveAll = function() {
  console.log(this._storage);
  //console.log(this._limit);
};

/******************************TESTS*******************************/

var hashT = new HashTable();

hashT.insert('Alex Hawkins', '510-599-1930');
//hashT.retrieve();
//[ , , , [ [ 'Alex Hawkins', '510-599-1930' ] ] ]
hashT.insert('Boo Radley', '520-589-1970');
//hashT.retrieve();
//[ , [ [ 'Boo Radley', '520-589-1970' ] ], , [ [ 'Alex Hawkins', '510-599-1930' ] ] ]
hashT.insert('Vance Carter', '120-589-1970').insert('Rick Mires', '520-589-1970').insert('Tom Bradey', '520-589-1970').insert('Biff Tanin', '520-589-1970');
//hashT.retrieveAll();
/* 
[ ,
  [ [ 'Boo Radley', '520-589-1970' ],
    [ 'Tom Bradey', '520-589-1970' ] ],
  ,
  [ [ 'Alex Hawkins', '510-599-1930' ],
    [ 'Rick Mires', '520-589-1970' ] ],
  ,
  ,
  [ [ 'Biff Tanin', '520-589-1970' ] ] ]
*/

//overide example (Phone Number Change)
//
hashT.insert('Rick Mires', '650-589-1970').insert('Tom Bradey', '818-589-1970').insert('Biff Tanin', '987-589-1970');
//hashT.retrieveAll();

/* 
[ ,
  [ [ 'Boo Radley', '520-589-1970' ],
    [ 'Tom Bradey', '818-589-1970' ] ],
  ,
  [ [ 'Alex Hawkins', '510-599-1930' ],
    [ 'Rick Mires', '650-589-1970' ] ],
  ,
  ,
  [ [ 'Biff Tanin', '987-589-1970' ] ] ]

*/

hashT.remove('Rick Mires');
hashT.remove('Tom Bradey');
//hashT.retrieveAll();

/* 
[ ,
  [ [ 'Boo Radley', '520-589-1970' ] ],
  ,
  [ [ 'Alex Hawkins', '510-599-1930' ] ],
  ,
  ,
  [ [ 'Biff Tanin', '987-589-1970' ] ] ]


*/

hashT.insert('Dick Mires', '650-589-1970').insert('Lam James', '818-589-1970').insert('Ricky Ticky Tavi', '987-589-1970');
hashT.retrieveAll();


/* NOTICE HOW HASH TABLE HAS NOW DOUBLED IN SIZE UPON REACHING 75% CAPACITY ie 6/8. It is now size 16.
 [,
  ,
  [ [ 'Vance Carter', '120-589-1970' ] ],
  [ [ 'Alex Hawkins', '510-599-1930' ],
    [ 'Dick Mires', '650-589-1970' ],
    [ 'Lam James', '818-589-1970' ] ],
  ,
  ,
  ,
  ,
  ,
  [ [ 'Boo Radley', '520-589-1970' ],
    [ 'Ricky Ticky Tavi', '987-589-1970' ] ],
  ,
  ,
  ,
  ,
  [ [ 'Biff Tanin', '987-589-1970' ] ] ]




*/
console.log(hashT.retrieve('Lam James'));  //818-589-1970
console.log(hashT.retrieve('Dick Mires')); //650-589-1970
console.log(hashT.retrieve('Ricky Ticky Tavi')); //987-589-1970
console.log(hashT.retrieve('Alex Hawkins')); //510-599-1930
console.log(hashT.retrieve('Lebron James')); //null

3
いい感じ。ここで、なぜこれが便利で、他のすべての回答よりも適しているのかについても説明してください。
Thomas Tempelmann 2016

1

次のようにして作成できます。

var dictionary = { Name:"Some Programmer", Age:24, Job:"Writing Programs"  };

//Iterate Over using keys
for (var key in dictionary) {
  console.log("Key: " + key + " , " + "Value: "+ dictionary[key]);
}

//access a key using object notation:
console.log("Her Name is: " + dictionary.Name)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.