在
JavaScript中,所有对象的行为有点像hashmaps.但是,这些hashmaps的键必须是字符串.如果没有,它们将被转换成toString().这意味着:
var a = {foo: 1}; var b = {bar: 2}; var o = {}; o[a] = 100; o[b]; // 100 JSON.stringify(o); // '{"[object Object]":100}'
也就是说,由于任何plain对象的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,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})
解决方法
这是一个快速的概念验证…
我几乎没有测试过,我确信会有一些不能处理的角落.
性能将会非常低效,因为__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 = {}; }