【问题标题】:Is there a hashmap library for JavaScript?有 JavaScript 的 hashmap 库吗?
【发布时间】:2011-03-19 14:16:24
【问题描述】:

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

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

也就是说,由于任何普通 Object 的 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序列化是顺序依赖的。

有没有好的库或技术来实现这样的哈希图?

更新:

等效地,是否有一个很好的散列函数:

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

【问题讨论】:

    标签: javascript hashmap


    【解决方案1】:

    这是一个老问题,但 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.stringifying 对象将无法区分 o1 和 o2。

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

    Traceur compiler 还没有 Map 和 Weakmap 的官方 polyfill,但是有一个开放的 pull request,其中包含两者的 polyfill。这些 polyfill 的代码(如果有人想单独添加它们)在这里:Map 和 WeakMap。假设这些 polyfill 运行良好,您可以立即开始使用 Map 或 WeakMap。 :)

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

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

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

        【讨论】:

        【解决方案4】:

        这是一个快速的概念验证...

        我几乎没有测试过它,而且我敢肯定会有它无法处理的极端情况。

        性能会非常低效,因为__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 = {};
        }
        

        【讨论】:

        • 是的,它需要一些工作才能提高性能和防弹,但我认为你的想法是正确的。
        • @Peeja:我不确定它是否可以在满足您的要求的同时提高性能。虽然,根据您的确切需求,也许它可以做得足够好。
        【解决方案5】:

        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。

        【讨论】:

        • 仅供参考,我提交了更改。上面的查询现在都将返回 200。
        【解决方案6】:

        我会向您推荐来自Tim Down 的jshashtable 项目。

        【讨论】:

        • jshashtable 是一个好的开始,但它仅在键是完全相同的对象时才有效。我希望能够使用不同但相等的对象(b 与上面代码中的c)。本质上,我需要一个库,它通过属性和值来提供对相等性的良好定义。
        • 我也会:)。在这种情况下,您需要一个自定义的equals 函数来比较两个对象的属性,或者作为对象的方法,或者更好地传递给Hashtable 构造函数:var objectsEqual = function(o1, o2) { for (var i in o1) { if (o1[i] !== o2[i]) { return false; } } for (i in o2) { if (!(i in o1)) { return false; } } return true; }; var hash = new Hashtable(null, objectsEqual);
        • Peeja:当您为 jshashtable 提供自己的相等函数时,它会处理这种情况,如我上面的评论(请原谅缺少格式;cmets 不允许更好)。
        • 没错,这行得通。对于我想到的用途,我真的需要使用hashCode 方法(为了性能)。你知道通过属性散列 JS 对象的好方法吗?这确实是我问题的核心。
        • 我在决定是否尝试为 jshashtable 实现默认哈希函数时考虑了这个问题。真的,在 JavaScript 中没有简单的方法。您可以编写一个函数来将对象中的所有属性提取到键值对数组中,按属性名称对其进行排序,然后对其进行 JSONify,但是一旦您在散列函数中完成所有这些操作,您可能会发现它更多高效不使用它。一种折衷方案可能是使用一个散列函数,该函数简单地将对象的一个​​或两个属性的值连接起来,这些属性在您的对象之间变化很大。
        【解决方案7】:

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

        【讨论】:

        • Prototype 的 Hash 实现无法将任意对象存储为键,OP 将获得与现在完全相同的结果,请检查 set method 和 @987654323 的实现@.
        【解决方案8】:

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

        【讨论】:

        • 这正是我想要做的。
        猜你喜欢
        • 2015-07-08
        • 2010-12-12
        • 1970-01-01
        • 2010-12-31
        • 1970-01-01
        • 2015-04-10
        • 1970-01-01
        • 2012-07-25
        相关资源
        最近更新 更多