417 Stimmen

JavaScript-Hashmap-Äquivalent

Wie bereits in Update 3 über diese Antwort diese Notation:

var hash = {};
hash[X]

das Objekt nicht wirklich hasht X ; es wandelt eigentlich nur um X zu einer Zeichenkette (über .toString() wenn es sich um ein Objekt handelt, oder einige andere eingebaute Konvertierungen für verschiedene primitive Typen) und sucht dann diese Zeichenkette, ohne sie zu hashen, in " hash ". Auch die Objektgleichheit wird nicht überprüft - wenn zwei verschiedene Objekte die gleiche Stringkonvertierung haben, überschreiben sie sich einfach gegenseitig.

Angesichts dieser - gibt es eine effiziente Implementierungen von Hashmaps in JavaScript?

(Zum Beispiel, das zweite Google-Ergebnis von javascript hashmap führt zu einer Implementierung, die für jede Operation O(n) ist. Verschiedene andere Ergebnisse ignorieren die Tatsache, dass verschiedene Objekte mit äquivalenten Zeichenkettendarstellungen sich gegenseitig überschreiben.

25voto

Oriol Punkte 246798

Sie können ECMAScript 6 verwenden WeakMap ou Map :

  • WeakMaps sind Schlüssel/Wert-Maps, bei denen die Schlüssel Objekte sind.

Map Objekte sind einfache Schlüssel/Wert-Maps. Jeder Wert (sowohl Objekte als auch primitive Werte) kann entweder als Schlüssel oder als Wert verwendet werden.

Beachten Sie, dass beides nicht allgemein unterstützt wird, aber Sie können die ECMAScript 6 Schablone (erfordert natives ECMAScript 5 oder ECMAScript 5 Schimmel ) zur Unterstützung Map , aber nicht WeakMap ( sehen warum ).

13voto

pottedmeat Punkte 3341

Sie müssten Paare von Objekt/Wertpaaren in einem internen Zustand speichern:

HashMap = function(){
  this._dict = [];
}

HashMap.prototype._get = function(key){
  for(var i=0, couplet; couplet = this._dict[i]; i++){
    if(couplet[0] === key){
      return couplet;
    }
  }
}

HashMap.prototype.put = function(key, value){
  var couplet = this._get(key);
  if(couplet){
    couplet[1] = value;
  }else{
    this._dict.push([key, value]);
  }
  return this; // for chaining
}
HashMap.prototype.get = function(key){
  var couplet = this._get(key);
  if(couplet){
    return couplet[1];
  }
}

Und verwenden Sie es als solches:

var color = {}; // Unique object instance
var shape = {}; // Unique object instance
var map = new HashMap();
map.put(color, "blue");
map.put(shape, "round");
console.log("Item is", map.get(color), "and", map.get(shape));

Natürlich liegt auch diese Implementierung in der Größenordnung von O(n). Eugene's Beispiele sind die einzige Möglichkeit, einen Hash zu erhalten, der mit einer Geschwindigkeit funktioniert, die man von einem echten Hash erwarten würde.

Ein anderer Ansatz, der der Antwort von Eugene entspricht, besteht darin, allen Objekten eine eindeutige ID zuzuordnen. Einer meiner Lieblingsansätze ist es, eine der eingebauten Methoden, die von der Oberklasse Object geerbt wurden, durch eine benutzerdefinierte Funktion zu ersetzen und diesem Funktionsobjekt Eigenschaften zuzuweisen. Wenn Sie meine HashMap-Methode umschreiben würden, um dies zu tun, würde sie wie folgt aussehen:

HashMap = function(){
  this._dict = {};
}

HashMap.prototype._shared = {id: 1};
HashMap.prototype.put = function put(key, value){
  if(typeof key == "object"){
    if(!key.hasOwnProperty._id){
      key.hasOwnProperty = function(key){
        return Object.prototype.hasOwnProperty.call(this, key);
      }
      key.hasOwnProperty._id = this._shared.id++;
    }
    this._dict[key.hasOwnProperty._id] = value;
  }else{
    this._dict[key] = value;
  }
  return this; // for chaining
}

HashMap.prototype.get = function get(key){
  if(typeof key == "object"){
    return this._dict[key.hasOwnProperty._id];
  }
  return this._dict[key];
}

Diese Version scheint nur geringfügig schneller zu sein, aber theoretisch wird sie bei großen Datensätzen deutlich schneller sein.

11voto

Michael Spector Punkte 35861

Leider war keine der vorherigen Antworten für meinen Fall geeignet: verschiedene Schlüsselobjekte können denselben Hash-Code haben. Daher habe ich eine einfache Java-ähnliche HashMap-Version geschrieben:

function HashMap() {
    this.buckets = {};
}

HashMap.prototype.put = function(key, value) {
    var hashCode = key.hashCode();
    var bucket = this.buckets[hashCode];
    if (!bucket) {
        bucket = new Array();
        this.buckets[hashCode] = bucket;
    }
    for (var i = 0; i < bucket.length; ++i) {
        if (bucket[i].key.equals(key)) {
            bucket[i].value = value;
            return;
        }
    }
    bucket.push({ key: key, value: value });
}

HashMap.prototype.get = function(key) {
    var hashCode = key.hashCode();
    var bucket = this.buckets[hashCode];
    if (!bucket) {
        return null;
    }
    for (var i = 0; i < bucket.length; ++i) {
        if (bucket[i].key.equals(key)) {
            return bucket[i].value;
        }
    }
}

HashMap.prototype.keys = function() {
    var keys = new Array();
    for (var hashKey in this.buckets) {
        var bucket = this.buckets[hashKey];
        for (var i = 0; i < bucket.length; ++i) {
            keys.push(bucket[i].key);
        }
    }
    return keys;
}

HashMap.prototype.values = function() {
    var values = new Array();
    for (var hashKey in this.buckets) {
        var bucket = this.buckets[hashKey];
        for (var i = 0; i < bucket.length; ++i) {
            values.push(bucket[i].value);
        }
    }
    return values;
}

Hinweis: Schlüsselobjekte müssen die Methoden hashCode() und equals() "implementieren".

6voto

Lambder Punkte 2832

Ich habe eine JavaScript-HashMap implementiert, deren Code man über http://github.com/lambder/HashMapJS/tree/master

Hier ist der Code:

/*
 =====================================================================
 @license MIT
 @author Lambder
 @copyright 2009 Lambder.
 @end
 =====================================================================
 */
var HashMap = function() {
  this.initialize();
}

HashMap.prototype = {
  hashkey_prefix: "<#HashMapHashkeyPerfix>",
  hashcode_field: "<#HashMapHashkeyPerfix>",

  initialize: function() {
    this.backing_hash = {};
    this.code = 0;
  },
  /*
   Maps value to key returning previous association
   */
  put: function(key, value) {
    var prev;
    if (key && value) {
      var hashCode = key[this.hashcode_field];
      if (hashCode) {
        prev = this.backing_hash[hashCode];
      } else {
        this.code += 1;
        hashCode = this.hashkey_prefix + this.code;
        key[this.hashcode_field] = hashCode;
      }
      this.backing_hash[hashCode] = value;
    }
    return prev;
  },
  /*
   Returns value associated with given key
   */
  get: function(key) {
    var value;
    if (key) {
      var hashCode = key[this.hashcode_field];
      if (hashCode) {
        value = this.backing_hash[hashCode];
      }
    }
    return value;
  },
  /*
   Deletes association by given key.
   Returns true if the association existed, false otherwise
   */
  del: function(key) {
    var success = false;
    if (key) {
      var hashCode = key[this.hashcode_field];
      if (hashCode) {
        var prev = this.backing_hash[hashCode];
        this.backing_hash[hashCode] = undefined;
        if(prev !== undefined)
          success = true;
      }
    }
    return success;
  }
}

//// Usage

// Creation

var my_map = new HashMap();

// Insertion

var a_key = {};
var a_value = {struct: "structA"};
var b_key = {};
var b_value = {struct: "structB"};
var c_key = {};
var c_value = {struct: "structC"};

my_map.put(a_key, a_value);
my_map.put(b_key, b_value);
var prev_b = my_map.put(b_key, c_value);

// Retrieval

if(my_map.get(a_key) !== a_value){
  throw("fail1")
}
if(my_map.get(b_key) !== c_value){
  throw("fail2")
}
if(prev_b !== b_value){
  throw("fail3")
}

// Deletion

var a_existed = my_map.del(a_key);
var c_existed = my_map.del(c_key);
var a2_existed = my_map.del(a_key);

if(a_existed !== true){
  throw("fail4")
}
if(c_existed !== false){
  throw("fail5")
}
if(a2_existed !== false){
  throw("fail6")
}

5voto

Nox73 Punkte 171

In ECMAScript 6 können Sie Folgendes verwenden WeakMap .

Beispiel:

var wm1 = new WeakMap(),
    wm2 = new WeakMap(),
    wm3 = new WeakMap();
var o1 = {},
    o2 = function(){},
    o3 = window;

wm1.set(o1, 37);
wm1.set(o2, "azerty");
wm2.set(o1, o2); // A value can be anything, including an object or a function
wm2.set(o3, undefined);
wm2.set(wm1, wm2); // Keys and values can be any objects. Even WeakMaps!

wm1.get(o2); // "azerty"
wm2.get(o2); // Undefined, because there is no value for o2 on wm2
wm2.get(o3); // Undefined, because that is the set value

wm1.has(o2); // True
wm2.has(o2); // False
wm2.has(o3); // True (even if the value itself is 'undefined')

wm3.set(o1, 37);
wm3.get(o1); // 37
wm3.clear();
wm3.get(o1); // Undefined, because wm3 was cleared and there is no value for o1 anymore

wm1.has(o1);   // True
wm1.delete(o1);
wm1.has(o1);   // False

Aber:

Da Referenzen schwach sind, sind die Schlüssel der WeakMap nicht aufzählbar (d.h. es gibt keine Methode, die eine Liste der Schlüssel liefert).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X