是否有JavaScript的哈希映射库?

时间:2010-07-22 14:12:01

标签: javascript hashmap

在JavaScript中,所有对象都有点像哈希映射。但是,这些哈希映射的关键字必须是字符串。如果不是,则会使用toString()进行转换。这意味着:

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],因此它们都处理相同的值。

我想创建一个hashmap,其中具有相同属性和值的对象寻址相同的值,但具有不同属性或值的对象会寻址不同的值。那就是:

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序列化依赖于顺序。

是否有一个很好的库或技术来实现这样的hashmap?

更新

等效地,是否存在良好的散列函数:

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

8 个答案:

答案 0 :(得分:8)

我会向您推荐jshashtableTim Down项目。

答案 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 = {};
}

答案 2 :(得分: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这些对象无法区分o1o2

MDN有more info。还有一个WeakMap,它不会维护对用作密钥的对象的引用,因此它们可能被垃圾收集。

Traceur compiler还没有针对Map和Weakmap的官方polyfill,但是有两个polyfill的open pull请求。这些polyfill的代码(如果有人想单独添加它们)在这里:MapWeakMap。假设这些polyfill工作正常,您可以立即开始使用Map或WeakMap。 :)

答案 3 :(得分:0)

您可以使用toString实现自己的类,以确保键的排序。

答案 4 :(得分:0)

Prototype的Hash很好地涵盖了http://prototypejs.org/api/hash

答案 5 :(得分: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。

答案 6 :(得分:0)

这个帖子实际上让我在我当前的项目中找到了一个bug。但是,现在已经解决了这个问题,我的项目中的HashMap实现(https://github.com/Airblader/jsava)将完全按照您的描述执行。与jshashtable不同,没有桶。

答案 7 :(得分:-2)

答案是使用两个数组,一个用于键,一个用于值。

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