Javascript哈希表使用对象键

Javascript哈希表使用对象键,javascript,object,hash,key,Javascript,Object,Hash,Key,我想创建一个哈希表,其中包含未转换为字符串的对象键 像这样的事情: var object1 = new Object(); var object2 = new Object(); var myHash = new HashTable(); myHash.put(object1, "value1"); myHash.put(object2, "value2"); alert(myHash.get(object1), myHash.get(object2

我想创建一个哈希表,其中包含未转换为字符串的
对象

像这样的事情:

var object1 = new Object();
var object2 = new Object();

var myHash = new HashTable();

myHash.put(object1, "value1");
myHash.put(object2, "value2");

alert(myHash.get(object1), myHash.get(object2)); // I wish that it will print value1 value2

编辑:查看完整解决方案

查找对象时只需使用严格相等运算符:
=

var objects = [];
objects.push(object1);
objects.push(object2);

objects[0] === object1; // true
objects[1] === object1; // false
实现将取决于您在
哈希表
类中存储对象的方式。

以下是一个建议:

function HashTable() {
    this.hashes = {};
}

HashTable.prototype = {
    constructor: HashTable,

    put: function( key, value ) {
        this.hashes[ JSON.stringify( key ) ] = value;
    },

    get: function( key ) {
        return this.hashes[ JSON.stringify( key ) ];
    }
};
API完全如您的问题所示

但是,您不能在js中使用引用(因此两个空对象在哈希表中看起来是一样的),因为您无法获得它。有关更多详细信息,请参阅此答案:

JSFIDLE演示:

但是,对于事物独特的一面,您可以使用原始对象,如下所示:

function HashTable() {
    this.hashes = {},
    this.id = 0;
}

HashTable.prototype = {
    constructor: HashTable,

    put: function( obj, value ) {
        obj.id = this.id;
        this.hashes[ this.id ] = value;
        this.id++;
    },

    get: function( obj ) {
        return this.hashes[ obj.id ];
    }
};
JSFIDLE演示:

这意味着您的对象需要有一个名为
id
的属性,您在其他地方不会使用该属性。如果您想将此属性作为不可枚举属性,我建议您查看一下(它不是跨浏览器的,但是,即使使用ES5 Shim,它在IE7中也不起作用)

这也意味着您可以在此哈希表中存储的项目数量有限。限于,就是

现在,“它在任何地方都不会工作”的解决方案是:使用ES6 WeakMaps。这样做的目的正是:将对象作为键。我建议您阅读MDN以了解更多信息:

但它与您的API略有不同(它是
set
而不是
put
):

带weakmap垫片的JSFIDLE演示:

但是,弱映射是在FF和Chrome中实现的(仅当您在Chrome中启用“实验javascript功能”标志时)。有可用的垫片,如下所示:。使用风险自负


您还可以使用
映射
,它们可能更适合您的需要,因为您还需要将基本值(字符串)存储为键

我把@Florian Margaine的建议提升到了更高的层次,并提出了以下建议:

function HashTable(){
    var hash = new Object();
    this.put = function(key, value){
        if(typeof key === "string"){
            hash[key] = value;
        }
        else{
            if(key._hashtableUniqueId == undefined){
                key._hashtableUniqueId = UniqueId.prototype.generateId();
            }
            hash[key._hashtableUniqueId] = value;
        }

    };

    this.get = function(key){
        if(typeof key === "string"){
            return hash[key];
        }
        if(key._hashtableUniqueId == undefined){
            return undefined;
        }
        return hash[key._hashtableUniqueId];
    };
}

function UniqueId(){

}

UniqueId.prototype._id = 0;
UniqueId.prototype.generateId = function(){
    return (++UniqueId.prototype._id).toString();
};
用法

var map = new HashTable();
var object1 = new Object();
map.put(object1, "Cocakola");
alert(map.get(object1)); // Cocakola

//Overriding
map.put(object1, "Cocakola 2");
alert(map.get(object1)); // Cocakola 2

// String key is used as String     
map.put("myKey", "MyValue");
alert(map.get("myKey")); // MyValue
alert(map.get("my".concat("Key"))); // MyValue

// Invalid keys 
alert(map.get("unknownKey")); // undefined
alert(map.get(new Object())); // undefined

我知道我晚了一年,但对于所有偶然发现这条线索的人,我已经将有序对象stringify编写成JSON,解决了上述难题:

我还在玩自定义哈希表实现,这也与主题相关:

//使用字符串化进行排序
var orderedStringify=函数(o,fn){
var-props=[];
var res='{';
for(变量i在o中){
道具推(一);
}
props=props.sort(fn);
对于(变量i=0;i
这里有一个建议,将@Florian的解决方案与@Laurent的解决方案相结合

function HashTable() {
    this.hashes = [];
}

HashTable.prototype = {
    constructor: HashTable,

    put: function( key, value ) {
        this.hashes.push({
            key: key,
            value: value
        });
    },

    get: function( key ) {
        for( var i = 0; i < this.hashes.length; i++ ){
            if(this.hashes[i].key == key){
                return this.hashes[i].value;
            }
        }
    }
};
函数哈希表(){
this.hashes=[];
}
HashTable.prototype={
构造函数:哈希表,
put:功能(键、值){
这是hash.push({
钥匙:钥匙,
价值:价值
});
},
获取:函数(键){
for(var i=0;i

它不会以任何方式更改您的对象,也不依赖JSON.stringify。

这里有一个简单的
Map
实现,它可以使用任何类型的键,包括对象引用,并且不会以任何方式更改键:

function Map() {
    var keys = [], values = [];

    return {
        put: function (key, value) {
            var index = keys.indexOf(key);
            if(index == -1) {
                keys.push(key);
                values.push(value);
            }
            else {
                values[index] = value;
            }
        },
        get: function (key) {
            return values[keys.indexOf(key)];
        }
    };
}
虽然这会产生与哈希表相同的功能,但实际上并不是使用哈希函数实现的,因为它会在数组上迭代,并且在最坏情况下的性能为O(n)。然而,对于绝大多数合理的用例来说,这根本不应该是一个问题。
indexOf
函数是由JavaScript引擎实现的,并且经过了高度优化。

使用
JSON.stringify()
对我来说完全不方便,客户端无法真正控制如何唯一地识别它们的密钥。用作键的对象应该有一个哈希函数,但我猜在大多数情况下重写
toString()
方法,以便它们返回唯一的字符串,会很好地工作:

var myMap = {};

var myKey = { toString: function(){ return '12345' }};
var myValue = 6;

// same as myMap['12345']
myMap[myKey] = myValue;
显然,
toString()
应该对对象的属性执行一些有意义的操作,以创建唯一的字符串。如果要强制密钥有效,可以创建一个包装器,并在
get()
put()
方法中添加如下检查:

if(!key.hasOwnProperty('toString')){
   throw(new Error('keys must override toString()'));
}
但是,如果您要完成那么多的工作,您还可以使用除
toString()
之外的其他工具;让你的意图更加清晰的东西。 因此,一个非常简单的建议是:

function HashTable() {
    this.hashes = {};
}

HashTable.prototype = {
    constructor: HashTable,

    put: function( key, value ) {
        // check that the key is meaningful, 
        // also will cause an error if primitive type
        if( !key.hasOwnProperty( 'hashString' ) ){
           throw( new Error( 'keys must implement hashString()' ) );
        }
        // use .hashString() because it makes the intent of the code clear
        this.hashes[ key.hashString() ] = value;
    },

    get: function( key ) {
        // check that the key is meaningful, 
        // also will cause an error if primitive type
        if( !key.hasOwnProperty( 'hashString' ) ){
           throw( new Error( 'keys must implement hashString()' ) );
        }
        // use .hashString() because it make the intent of the code clear
        return this.hashes[ key.hashString()  ];
    }
};

受@florian的启发,这里提供了一种id不需要JSON的方式

'use strict';

module.exports = HashTable;

function HashTable () {
  this.index = [];
  this.table = [];
}

HashTable.prototype = {

  constructor: HashTable,

  set: function (id, key, value) {
    var index = this.index.indexOf(id);
    if (index === -1) {
      index = this.index.length;
      this.index.push(id);
      this.table[index] = {};
    }
    this.table[index][key] = value;
  },

  get: function (id, key) {
    var index = this.index.indexOf(id);
    if (index === -1) {
      return undefined;
    }
    return this.table[index][key];
  }

};

我采用@Ilya_Gazman解决方案,并通过将“_hashtableUniqueId”设置为不可枚举属性(它不会出现在JSON请求中,也不会在for循环中列出)对其进行改进。还删除了UniqueId对象,因为只使用HastTable函数闭包就足够了。有关使用详情,请参阅Ilya_Gazman邮报

function HashTable() {
   var hash = new Object();

   return {
       put: function (key, value) {
           if(!HashTable.uid){
               HashTable.uid = 0;
           }
           if (typeof key === "string") {
               hash[key] = value;
           } else {
               if (key._hashtableUniqueId === undefined) {
                   Object.defineProperty(key, '_hashtableUniqueId', {
                       enumerable: false,
                       value: HashTable.uid++
                   });
               }
               hash[key._hashtableUniqueId] = value;
           }
       },
       get: function (key) {
           if (typeof key === "string") {
               return hash[key];
           }
           if (key._hashtableUniqueId === undefined) {
               return undefined;
           }
           return hash[key._hashtableUniqueId];
       }
   };
}

最好的解决方案是在您可以的时候使用(即当您以浏览为目标时)
'use strict';

module.exports = HashTable;

function HashTable () {
  this.index = [];
  this.table = [];
}

HashTable.prototype = {

  constructor: HashTable,

  set: function (id, key, value) {
    var index = this.index.indexOf(id);
    if (index === -1) {
      index = this.index.length;
      this.index.push(id);
      this.table[index] = {};
    }
    this.table[index][key] = value;
  },

  get: function (id, key) {
    var index = this.index.indexOf(id);
    if (index === -1) {
      return undefined;
    }
    return this.table[index][key];
  }

};
function HashTable() {
   var hash = new Object();

   return {
       put: function (key, value) {
           if(!HashTable.uid){
               HashTable.uid = 0;
           }
           if (typeof key === "string") {
               hash[key] = value;
           } else {
               if (key._hashtableUniqueId === undefined) {
                   Object.defineProperty(key, '_hashtableUniqueId', {
                       enumerable: false,
                       value: HashTable.uid++
                   });
               }
               hash[key._hashtableUniqueId] = value;
           }
       },
       get: function (key) {
           if (typeof key === "string") {
               return hash[key];
           }
           if (key._hashtableUniqueId === undefined) {
               return undefined;
           }
           return hash[key._hashtableUniqueId];
       }
   };
}
// Run this in the beginning of your app (or put it into a file you just import)
(enableObjectID)();

const uniqueId: symbol = Symbol('The unique id of an object');

function enableObjectID(): void {
    if (typeof Object['id'] !== 'undefined') {
        return;
    }

    let id: number = 0;

    Object['id'] = (object: any) => {
        const hasUniqueId: boolean = !!object[uniqueId];
        if (!hasUniqueId) {
            object[uniqueId] = ++id;
        }

        return object[uniqueId];
    };
}
let objectA = {};
let objectB = {};
let dico = {};

dico[(<any>Object).id(objectA)] = "value1";

// or 

dico[Object['id'](objectA);] = "value1";

// If you are not using typescript you don't need the casting

dico[Object.id(objectA)] = "value1"
ObjectMap = function() {
    this.keys = [];
    this.values = [];
}

ObjectMap.prototype.set = function(key, value) {
    var index = this.keys.indexOf(key);
    if (index == -1) {
        this.keys.push(key);
        this.values.push(value);
    } else {
        this.values[index] = value;
    }
}

ObjectMap.prototype.get = function(key) {
    return this.values[ this.keys.indexOf(key) ];
}

ObjectMap.prototype.exists = function(key) {
    return this.keys.indexOf(key) != -1;
}

/*
    TestObject = function() {}

    testA = new TestObject()
    testB = new TestObject()

    om = new ObjectMap()
    om.set(testA, true)
    om.get(testB)
    om.exists(testB)
    om.exists(testA)
    om.exists(testB)
*/