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)
*/