2010-07-22 15 views
10

In JavaScript verhalten sich alle Objekte wie hashmaps. Die Schlüssel zu diesen hashmaps müssen jedoch Strings sein. Wenn nicht, werden sie mit toString() konvertiert. Das heißt:Gibt es eine hashmap-Bibliothek für JavaScript?

var a = {foo: 1}; 
var b = {bar: 2}; 
var o = {}; 
o[a] = 100; 
o[b];    // 100 
JSON.stringify(o); // '{"[object Object]":100}' 

Das heißt, da die toString() jeder Ebene Objekt ist [object Object], sie alle den gleichen Wert adressieren.

Ich möchte eine Hashmap erstellen, wobei Objekte mit denselben Eigenschaften und Werten den gleichen Wert adressieren, Objekte mit unterschiedlichen Eigenschaften oder Werten jedoch unterschiedliche Werte haben. Das heißt:

var a = {foo: 1}; 
var b = {bar: 2, baz: 3}; 
var c = {baz: 3, bar: 2}; 
var hash = new Hash(); 
hash.set(a, 100); 
hash.get(b);  // undefined 
hash.set(b, 200); 
hash.get(b);  // 200 
hash.get(c);  // 200 

Mein erster Instinkt war JSON.stringify() zu verwenden, um Objekte in Strings zu drehen, aber:

var hash = {}; 
var b = {bar: 2, baz: 3}; 
var c = {baz: 3, bar: 2}; 
hash[JSON.stringify(b)] = 100 
hash[JSON.stringify(b)] // 100 
hash[JSON.stringify(c)] // undefined 
JSON.stringify(b)  // '{"bar":2,"baz":3}' 
JSON.stringify(c)  // '{"baz":3,"bar":2}' 

Das heißt, JSON Serialisierung ist auftragsabhängig.

Gibt es eine gute Bibliothek oder Technik, um eine Hashmap so zu implementieren?

aktualisieren:

Gleichwertig, gibt es eine gute Hash-Funktion, so dass:

hash({foo: 1, bar: 2}) == hash({bar: 2, foo: 1}) 

Antwort

2

Hier ist ein schneller Proof-of-concept ...

ich es kaum überhaupt getestet habe, und ich bin sicher, dass es Ecke Fälle sein, dass es nicht mit umgehen kann.

Die Leistung wird schrecklich ineffizient sein, da die __createHash Funktion muss durch die Mitglieder aller Objekte recurse und dann sortieren, um einen "Hash" zu generieren, der Ihren Anforderungen entspricht.

HashMap = function() { 
    this.get = function(key) { 
     var hash = this.__createHash(key); 
     return this.__map[hash]; 
    }; 

    this.set = function(key, value) { 
     var hash = this.__createHash(key); 
     this.__map[hash] = value; 
    }; 

    this.__createHash = function(key) { 
     switch (typeof key) { 
      case 'function': 
       return 'function'; 

      case 'undefined': 
       return 'undefined'; 

      case 'string': 
       return '"' + key.replace('"', '""') + '"'; 

      case 'object': 
       if (!key) { 
        return 'null'; 
       } 

       switch (Object.prototype.toString.apply(key)) { 
        case '[object Array]': 
         var elements = []; 
         for (var i = 0; i < key.length; i++) { 
          elements.push(this.__createHash(key[i])); 
         } 
         return '[' + elements.join(',') + ']'; 

        case '[object Date]': 
         return '#' + key.getUTCFullYear().toString() 
            + (key.getUTCMonth() + 1).toString() 
            + key.getUTCDate().toString() 
            + key.getUTCHours().toString() 
            + key.getUTCMinutes().toString() 
            + key.getUTCSeconds().toString() + '#'; 

        default: 
         var members = []; 
         for (var m in key) { 
          members.push(m + '=' + this.__createHash(key[m])); 
         } 
         members.sort(); 
         return '{' + members.join(',') + '}'; 
       } 

      default: 
       return key.toString(); 
     } 
    }; 

    this.__map = {}; 
} 
+0

Ja, es braucht etwas Arbeit, um leistungsfähig und kugelsicher zu sein, aber ich denke, du hast die richtige Idee. – Peeja

+0

@Peeja: Ich bin mir nicht sicher, dass es leistungsfähig gemacht werden kann, während es immer noch Ihren Anforderungen entspricht. Obwohl, abhängig von Ihren genauen Bedürfnissen, kann es vielleicht gut genug gemacht werden. – LukeH

0

Sie könnten Ihre eigene Klasse mit einem toString implementieren, die eine Ordnung der Schlüssel gewährleistet.

+0

Das ist genau das, was ich versuche zu tun. – Peeja

0

Prototyp hat Hash ganz gut http://prototypejs.org/api/hash bedeckt

+1

Die 'Hash'-Implementierung des Prototyps funktioniert nicht, um ein beliebiges Objekt als Schlüssel zu speichern, das OP wird genau die gleichen Ergebnisse haben wie jetzt, überprüfe die Implementierung der ['set' Methode] (http: // github. com/sstephenson/prototype/blob/master/src/lang/hash.js # L124) und [dieses Beispiel] (http://jsbin.com/imeqa3/edit). – CMS

8

Ich würde Sie das jshashtable Projekt von Tim Down empfehlen.

+1

jshashtable ist ein guter Anfang, aber es funktioniert nur, wenn der Schlüssel das exakt gleiche Objekt ist. Ich möchte in der Lage sein, ein anderes, aber gleiches Objekt zu verwenden ("b" gegen "c" im obigen Code). Im Wesentlichen brauche ich eine Bibliothek, die eine gute Definition der Gleichheit durch Eigenschaften und Werte bietet. – Peeja

+1

Wie würde ich :). In diesem Fall benötigen Sie eine benutzerdefinierte "equals" -Funktion, die die Eigenschaften von zwei Objekten vergleicht, entweder als eine Methode Ihrer Objekte oder, besser gesagt, in den 'Hashtable'-Konstruktor:' var objectsEqual = function (o1, o2) {für (var i in o1) {if (o1 [i]! == o2 [i]) {return false; }} für (i in o2) {if (! (i in o1)) {gibt false zurück; }} gebe wahr zurück; }; var hash = new Hashtable (null, objectsEqual); ' –

+0

Peeja: jshashtable behandelt diesen Fall, wenn Sie es mit Ihrer eigenen Gleichheitsfunktion versehen, wie in meinem Kommentar oben (verzeihen Sie den Mangel an Formatierung; Kommentare erlauben es nicht besser). –

0

könnte geändert werden, um Objekte mit denselben Eigenschaften (aber in anderer Reihenfolge) als identisch zu behandeln, wenn eine Indexsuche durchgeführt wird.

Wenn Sie das Objekt {bar: 2, baz: 3, val: 200} in Ihrer Liste, und Sie haben setzen vorher eine passende Index auf einer Jörder Tabelle wie folgt aus:

var table = jOrder(data) 
    .index('signature', ['bar', 'baz'], {grouped: true}); 

Dann jetzt table.where([{bar: 2, baz: 3}])[0].val 200 zurück, aber table.where([{baz: 3, bar: 2}])[0].val wird nicht . Es würde jedoch nicht viel Mühe kosten, es nicht von der Reihenfolge der Eigenschaften abhängig zu machen. Lassen Sie es mich wissen, wenn Sie interessiert sind und ich werde die Reparatur so schnell wie möglich an GitHub weitergeben.

+0

FYI Ich habe die Änderung eingereicht. Die obigen Abfragen geben beide jetzt 200 zurück. –

-2

Die Antwort ist, zwei Arrays zu verwenden, eins für Schlüssel, eins für Werte.

var i = keys.indexOf(key) 
if (i == -1) 
    {keys.push(key); values.push(value)} 
else 
    {values[i] = value; } 
+0

Ich bin mir nicht sicher, ob ich das verstehe. Was soll dieser Code tun? – Peeja

+0

Bitte nicht. Sie werden einen Alptraum erleben. Benchmark hier: http://people.iola.dk/arj/2012/05/30/hashtables-in-javascript/ –

+0

Dies beantwortet nicht die ursprüngliche Frage, die zum Erstellen von hashmaps mit Objektschlüsseln dient. – FoolishSeth

0

Dieser Thread führte mich tatsächlich zu einem Fehler in meinem aktuellen Projekt. Dies ist jedoch jetzt behoben und meine HashMap-Implementierung in meinem Projekt (https://github.com/Airblader/jsava) wird genau das tun, was Sie beschrieben haben. Im Gegensatz zu jshashtable gibt es keine Buckets.

2

Dies ist eine alte Frage, aber ES6 verfügt über eine Funktion, die relevant sein können: Map

Karten beliebiger Objekte als Schlüssel und beliebige Werte als Werte haben können. Der Hauptunterschied besteht darin, dass die als Schlüssel verwendeten Objekte auch dann eindeutig sind, wenn die Objekte identisch sind.

Hier ist ein Beispiel:

var map = new WeakMap(); 

var o1 = {x: 1}; 
var o2 = {x: 1}; 

map.set(o1, 'first object'); 
map.set(o2, 'second object'); 

// The keys are unique despite the objects being identical. 

map.get(o1); // 'first object' 
map.get(o2); // 'second object' 
map.get({x: 1}); // undefined 

JSON.stringify die Objekte ing nicht zwischen o1 und o2 unterscheiden würde.

MDN hat more info. Es gibt auch eine WeakMap, die keine Verweise auf die als Schlüssel verwendeten Objekte enthält, so dass sie als Garbage Collected betrachtet werden können.

Die Traceur compiler hat noch keine offiziellen Polyfills für Map und Weakmap, aber es gibt eine offene Pull-Anfrage mit Polyfills für beide. Der Code für diese Polyfills (falls jemand sie einzeln hinzufügen möchte) ist hier: Map und WeakMap. Wenn diese Polyfills gut funktionieren, können Sie heute Map oder WeakMap verwenden. :)

Verwandte Themen