2010-07-22 3 views
10

В JavaScript все объекты действуют как хэш-карты. Однако ключи к этим хэш-картам должны быть строками. Если это не так, они преобразуются с toString(). Это означает, что:Есть ли библиотека hashmap для JavaScript?

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

То есть, так как toString() любого простого объект является [object Object], все они относятся к тому же значению.

Я хотел бы создать хэш-карту, где объекты с одинаковыми свойствами и значениями обращаются к одному и тому же значению, но объекты с разными свойствами или значениями адресуют разные значения. То есть:

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 

Мой первый инстинкт должен был использовать JSON.stringify() превратить объекты в строки, но:

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}' 

То есть, JSON сериализации зависящих от порядка.

Есть ли хорошая библиотека или техника для реализации хэш-карты?

Update:

Эквивалентного, есть хорошая функция хеширования, что:

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

ответ

2

Вот краткое понятие корректуры из-...

Я едва протестировал его на всех, и я уверен, что там будут уголки случаи, что он не может иметь дело.

Производительность будет ужасно неэффективной, потому что функция __createHash должна перезаписывать элементы любых объектов, а затем сортировать их, чтобы генерировать «хэш», который соответствует вашим требованиям.

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

Да, это требует некоторой работы, чтобы быть перфомантной и пуленепробиваемой, но я думаю, что у вас есть правильная идея. – Peeja

+0

@Peeja: Я не уверен, что это может быть выполнено, несмотря на то, что оно соответствует вашим требованиям. Хотя, в зависимости от ваших точных потребностей, возможно, это может быть сделано достаточно хорошо. – LukeH

0

Вы могли бы реализовать свой собственный класс с toString, который обеспечивает упорядоченность ключей.

+0

Это именно то, что я пытаюсь сделать. – Peeja

0

Прототип имеет Hash довольно красиво покрыты http://prototypejs.org/api/hash

+1

Реализация прототипа 'Hash' не будет работать для хранения произвольного объекта в качестве ключа, OP будет иметь точно такие же результаты, которые есть сейчас, проверьте реализацию метода' 'set' '(http: // github. com/sstephenson/prototype/blob/master/src/lang/hash.js # L124) и [этот пример] (http://jsbin.com/imeqa3/edit). – CMS

8

Я бы порекомендовал вам проект jshashtable от Tim Down.

+1

jshashtable - хороший старт, но он работает только тогда, когда ключ является одним и тем же объектом. Я хочу иметь возможность использовать другой, но равный объект ('b' vs.' c' в коде выше). По сути, мне нужна библиотека, которая обеспечивает хорошее определение равенства по свойствам и значениям. – Peeja

+1

Как бы я :). В этом случае вам понадобится пользовательская функция 'equals', которая сравнивает свойства двух объектов либо как метод ваших объектов, либо, лучше, передается в конструктор' Hashtable': 'var objectsEqual = function (o1, o2) {for (var i in o1) {if (o1 [i]! == o2 [i]) {return false; }} для (i в o2) {if (! (i в o1)) {return false; }} return true; }; var hash = new Hashtable (null, objectsEqual); ' –

+0

Peeja: jshashtable обрабатывает этот случай, когда вы предоставляете ему свою собственную функцию равенства, как в моем комментарии выше (простить отсутствие форматирования, комментарии не позволяют лучше). –

0

jOrder может быть изменен для обработки объектов с одинаковыми свойствами (но в другом порядке) как идентичные при выполнении поиска индекса.

Если у вас есть объект {bar: 2, baz: 3, val: 200} в списке, и вы ранее положил фитинга индекс на столе jOrder, как это:

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

Тогда прямо сейчас table.where([{bar: 2, baz: 3}])[0].val возвратит 200, но table.where([{baz: 3, bar: 2}])[0].val не будет , Это не потребует больших усилий, чтобы не зависеть от порядка свойств. Дайте мне знать, если вы заинтересованы, и я исправлю решение GitHub, как только смогу.

+0

FYI Я представил изменение. Запросы выше возвратят 200 сейчас. –

-2

Ответ заключается в использовании двух массивов, один для ключей, один для значений.

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

Я не уверен, что понимаю. Что означает этот код? – Peeja

+0

Пожалуйста, не надо. У вас будет кошмар производительности. Бенчмарк здесь: http://people.iola.dk/arj/2012/05/30/hashtables-in-javascript/ –

+0

Это не отвечает на исходный вопрос, связанный с созданием хэш-карт с объектными ключами. – FoolishSeth

0

Эта нить фактически привела меня к обнаружению ошибки в моем текущем проекте. Тем не менее, теперь это исправлено, и моя реализация HashMap в моем проекте (https://github.com/Airblader/jsava) сделает именно то, что вы описали. В отличие от jshashtable, нет ведер.

2

Это старый вопрос, но ES6 имеет особенность, которая может иметь отношение: Map

Карты могут иметь произвольные объекты в качестве ключей, и произвольных значений в качестве значений. Основное отличие состоит в том, что объекты, используемые в качестве ключей, остаются уникальными, даже если объекты идентичны.

Вот пример:

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 ИНГИ объектов не было бы в состоянии различать o1 и o2.

MDN имеет more info. Также есть WeakMap, который не поддерживает ссылки на объекты, используемые в качестве ключей, поэтому они могут быть собраны в мусор.

Traceur compiler еще не имеет официальных полисов для Карты и Weakmap, но есть открытый запрос на растяжение с полиполками для обоих. Код для этих полифиллов (в случае, если кто-то хочет их добавить отдельно): Map и WeakMap. Предполагая, что эти полисы хорошо работают, вы можете начать использовать Map или WeakMap сегодня. :)

Смежные вопросы