【问题标题】:Javascript HashTable use Object keyJavascript HashTable 使用 Object 键
【发布时间】:2012-06-09 04:14:10
【问题描述】:

我想使用未转换为字符串的Object 键创建一个哈希表。

类似这样的事情:

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

编辑:完整解决方案请参见my answer

【问题讨论】:

标签: javascript object hash key


【解决方案1】:
class Dict{
    constructor(){
        this.keys = [];
        this.values = [];
        this.set = this.set.bind(this);
    }

    set(key, value){
        this.keys.push(key);
        this.values.push(value);
    }

    get(key){
        return this.values[this.keys.indexOf(key)];
    }

    all(){
        return this.keys.map((kk, ii)=>[kk, this.values[ii]]);
    }
}

let d1 = new Dict();

let k1 = {1: 'a'};
d1.set(k1, 2);
console.log(d1.get(k1));  // 2
let k2 = {2: 'b'};
d1.set(k2, 3);


console.log(d1.all());
// [ [ { '1': 'a' }, 2 ], [ { '2': 'b' }, 3 ] ]

【讨论】:

    【解决方案2】:

    我知道我迟到了,但这里有一个简单的 HashMap 实现:

    Function.prototype.toJSON = Function.prototype.toString;
    //taken from https://stackoverflow.com/questions/1249531/how-to-get-a-javascript-objects-class
    function getNativeClass(obj) {
        if (typeof obj === "undefined") return "undefined";
        if (obj === null) return "null";
        return Object.prototype.toString.call(obj).match(/^\[object\s(.*)\]$/)[1];
    }
    function globals() {
        if (typeof global === "object") //node
            return global;
        return this;
    }
    
    function lookup(x) {
        return globals()[x];
    }
    
    function getAnyClass(obj) {
        if (typeof obj === "undefined") return "undefined";
        if (obj === null) return "null";
        return obj.constructor.name;
    }
    
    //taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#examples
    var getCircularReplacer = () => {
        const seen = new WeakSet();
        return (key, value) => {
            if (typeof value === "object" && value !== null) {
                if (seen.has(value)) {
                    return "[Circular]";
                }
                seen.add(value);
            }
            return value;
        };
    };
    
    
    
    function encode(x) {
        if (typeof x === "object" && x !== null) {
            var y = myClone(x);
            x = Object.getPrototypeOf(x);
            for (var i = 0; i < Object.getOwnPropertyNames(y).length; i++) { //Make enumerable
                x[Object.getOwnPropertyNames(y)[i]] = y[Object.getOwnPropertyNames(y)[i]];
            }
        }
    
        return getAnyClass(x) + " " + JSON.stringify(x, getCircularReplacer());
    }
    
    function decode(x) {
        var a = x.split(" ").slice(1).join(" "); //OBJECT
        if (typeof lookup(x.split(" ")[0])) {
            return new (lookup(x.split(" ")[0]))(JSON.parse(a))
        } else {
            return JSON.parse(a);
        }
    }
    
    
    //taken from https://github.com/feross/fromentries/blob/master/index.js
    /*! fromentries. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
    function fromEntries(iterable) {
        return [...iterable].reduce((obj, [key, val]) => {
            obj[key] = val
            return obj
        }, {})
    }
    
    
    var toEnumerable = (obj) => {
        return fromEntries(
            Object.getOwnPropertyNames(obj).map(prop => [prop, obj[prop]])
        );
    };
    
    
    //taken from https://stackoverflow.com/questions/41474986/how-to-clone-a-javascript-es6-class-instance
    function myClone(instanceOfBlah) {
        if (typeof instanceOfBlah !== "object" || !instanceOfBlah) { return instanceOfBlah; }
        const clone = Object.assign({}, toEnumerable(instanceOfBlah));
        const Blah = instanceOfBlah.constructor;
        Object.setPrototypeOf(clone, Blah.prototype);
        return clone;
    }
    
    function HashMap(a) {
        if (typeof a === "undefined") {
            a = [];
        }
    
        a = Array.from(a);
    
        a = a.map((e) => [encode(e[0]), e[1]]);
    
        this.a = a;
    }
    
    
    HashMap.from = function (a) {
        var temp = myClone(a);
        //convert to array
        a = [];
        for (var i = 0; i < Object.getOwnPropertyNames(temp).length; i++) {
            a.push([Object.getOwnPropertyNames(temp)[i], temp[Object.getOwnPropertyNames(temp)[i]]]);
        }
        return new HashMap(a);
    }
    
    HashMap.prototype.put = function (x, y) {
        this.a.push([encode(x), y]);
    }
    
    HashMap.prototype.get = function (x) {
        var t1 = this.a.map((e) => e[0]);
        return this.a[t1.indexOf(encode(x))][1];
    }
    
    HashMap.prototype.length = function () {
        return this.a.length;
    }
    
    HashMap.prototype.toString = function () {
        var result = [];
        for (var i = 0; i < this.length(); i++) {
            result.push(JSON.stringify(decode(this.a[i][0]), getCircularReplacer()) + " => " + this.a[i][1]);
        }
    
    
        return "HashMap {" + result + "}";
    }
    
    
    
    var foo = new HashMap();
    foo.put("SQRT3", Math.sqrt(3));
    foo.put({}, "bar");
    
    console.log(foo.get({}));
    console.log(foo.toString());

    请注意,它是有序的。 方法:

    • put:添加项目
    • get:访问一个项目
    • from(静态):从 JavaScript 对象转换
    • toString: 转成字符串

    缩小且未经测试:

    function getNativeClass(t){return void 0===t?"undefined":null===t?"null":Object.prototype.toString.call(t).match(/^\[object\s(.*)\]$/)[1]}function globals(){return"object"==typeof global?global:this}function lookup(t){return globals()[t]}function getAnyClass(t){return void 0===t?"undefined":null===t?"null":t.constructor.name}Function.prototype.toJSON=Function.prototype.toString;var getCircularReplacer=()=>{const t=new WeakSet;return(e,r)=>{if("object"==typeof r&&null!==r){if(t.has(r))return"[Circular]";t.add(r)}return r}};function encode(t){if("object"==typeof t&&null!==t){var e=myClone(t);t=Object.getPrototypeOf(t);for(var r=0;r<Object.getOwnPropertyNames(e).length;r++)t[Object.getOwnPropertyNames(e)[r]]=e[Object.getOwnPropertyNames(e)[r]]}return getAnyClass(t)+" "+JSON.stringify(t,getCircularReplacer())}function decode(t){var e=t.split(" ").slice(1).join(" ");return lookup(t.split(" ")[0]),new(lookup(t.split(" ")[0]))(JSON.parse(e))}function fromEntries(t){return[...t].reduce((t,[e,r])=>(t[e]=r,t),{})}var toEnumerable=t=>fromEntries(Object.getOwnPropertyNames(t).map(e=>[e,t[e]]));function myClone(t){if("object"!=typeof t||!t)return t;const e=Object.assign({},toEnumerable(t)),r=t.constructor;return Object.setPrototypeOf(e,r.prototype),e}function HashMap(t){void 0===t&&(t=[]),t=(t=Array.from(t)).map(t=>[encode(t[0]),t[1]]),this.a=t}HashMap.from=function(t){var e=myClone(t);t=[];for(var r=0;r<Object.getOwnPropertyNames(e).length;r++)t.push([Object.getOwnPropertyNames(e)[r],e[Object.getOwnPropertyNames(e)[r]]]);return new HashMap(t)},HashMap.prototype.put=function(t,e){this.a.push([encode(t),e])},HashMap.prototype.get=function(t){var e=this.a.map(t=>t[0]);return this.a[e.indexOf(encode(t))][1]},HashMap.prototype.length=function(){return this.a.length},HashMap.prototype.toString=function(){for(var t=[],e=0;e<this.length();e++)t.push(JSON.stringify(decode(this.a[e][0]),getCircularReplacer())+" => "+this.a[e][1]);return"HashMap {"+t+"}"};
    

    此外,您还可以通过更改 encodedecode 函数来自定义编码器和解码器。

    正如弗洛里安的回答,你不能在 js 中使用引用(所以两个空对象看起来与哈希表相同)。

    【讨论】:

      【解决方案3】:

      基于 Peters 的回答,但具有适当的类设计(不滥用闭包),因此这些值是可调试的。从Map 重命名为ObjectMap,因为Map 是一个内置函数。还添加了exists方法:

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

      【讨论】:

      • 我使用闭包来强制封装——我们正在创建一个抽象。底层数据结构只能使用我们的显式公共接口是可变的。这减少了应用程序代码中出现错误/错误/误用的可能性。我们仍然可以在调试器中调试内部,或者添加额外的便利方法,例如hasKey()size()
      • @Peter 它只是混淆了理解,现在允许新开发人员通过 F12/devtools 真正理解内部结构。你只是认为你做了一件好事,但实际上它阻碍了理解,因此导致了错误。
      • 抽象背后的整个想法是,它允许我们在更高层次上对我们试图解决的问题进行推理。 Map 定义了一个合约,允许我们减轻一定的精神负担。把它想象成任何其他内置的 JS 引擎。软件系统都是关于在抽象之上构建抽象,直到我们可以用“我们想要什么”而不是“如何”来表达。这确实是防止大型项目(拥有数百名贡献者)崩溃的唯一因素。
      • 哦,真的吗?我在 GitHub 上使用大型 WebGL/PBR/Game 引擎,它公开了 everything 用于调试目的。你猜怎么着,它做得很好,远没有崩溃。只是在他们修复代码之前不要合并糟糕的拉取请求......
      • 对不起,我不是故意的。你是对的——诸如核心库、游戏引擎、内核、设备驱动程序等所有东西都会做出这些权衡——并且有充分的理由——以最大限度地提高性能。这对于一支技术精湛、纪律严明的团队来说是可能的。我说的是尽可能降低风险。
      【解决方案4】:

      最好的解决方案是尽可能使用WeakMap(即当您的目标浏览器支持它时)

      否则,您可以使用以下解决方法(Typescript 编写且碰撞安全):

      // 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"
      

      【讨论】:

        【解决方案5】:

        我采用了@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];
               }
           };
        }
        

        【讨论】:

        • 如果我将一个项目映射到两个不同的 HashTable 映射中怎么办?您将获得重复的 id...为避免这种情况,我使用了 UniqueId 对象。用 jsons 很好。
        • @Ilya_Gazman 感谢您注意到这一点!,修复了上面的代码。在我的 AngularJS 实现中没有这个问题,因为工厂方法中有一个额外的闭包。
        【解决方案6】:

        我将@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
        

        【讨论】:

        • 很高兴你把它整理好了:)
        • 这要求您在调用put() 时始终保留对用作键的对象的引用。如果您要这样做,为什么首先要映射这些值?为什么不只保留对值的引用呢?您是否考虑过如果您有两个等效但不相同的对象 (!==) 会发生什么?它们应该散列到相同的值,但 get() 方法会因为任何不是 put() 中使用的确切对象的有效键而失败,因为您只将 ._hashtableUniqueId 属性添加到其中之一。
        • 不错的解决方案!!我通过使用 Object.defineProperty 将 _hashtableUniqueId 添加到键并将其配置为不可枚举来改进此解决方案,因此它不会出现在例如 json 请求中。也不需要 UniqueID 对象。详情可以看我的回答。
        • 不@ShawnMoore 会好的。你会得到 "101"._hashtableUniqueId 等于某个随机数
        【解决方案7】:

        受@florian 启发,这里有一种不需要 id 的方式JSON.stringify

        '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];
          }
        
        };
        

        【讨论】:

          【解决方案8】:

          使用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()  ];
              }
          };
          

          【讨论】:

          【解决方案9】:

          这是一个简单的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引擎实现,经过高度优化。

          【讨论】:

          • 致未来的读者:注意Map 不再是一个好名字,因为它与built-in Map type 冲突。
          【解决方案10】:

          这是一个提议,将@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;
                      }
                  }
              }
          };
          

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

          【讨论】:

          • 这个问题,它不是一个“哈希”表吗?没有散​​列。您刚刚为数组创建了一个误导性的包装器。另外,由于你使用了“==”,所以在使用混合类型作为键informit.com/articles/article.aspx?p=1997934&seqNum=5时会得到意想不到的结果。
          【解决方案11】:

          我知道我迟到了一年,但是对于所有其他偶然发现这个线程的人,我已经将有序对象 stringify 编写为 JSON,从而解决了上述难题:http://stamat.wordpress.com/javascript-object-ordered-property-stringify/

          我还在玩与主题相关的自定义哈希表实现:http://stamat.wordpress.com/javascript-quickly-find-very-large-objects-in-a-large-array/

          //SORT WITH STRINGIFICATION
          
          var orderedStringify = function(o, fn) {
              var props = [];
              var res = '{';
              for(var i in o) {
                  props.push(i);
              }
              props = props.sort(fn);
          
              for(var i = 0; i < props.length; i++) {
                  var val = o[props[i]];
                  var type = types[whatis(val)];
                  if(type === 3) {
                      val = orderedStringify(val, fn);
                  } else if(type === 2) {
                      val = arrayStringify(val, fn);
                  } else if(type === 1) {
                      val = '"'+val+'"';
                  }
          
                  if(type !== 4)
                      res += '"'+props[i]+'":'+ val+',';
              }
          
              return res.substring(res, res.lastIndexOf(','))+'}';
          };
          
          //orderedStringify for array containing objects
          var arrayStringify = function(a, fn) {
              var res = '[';
              for(var i = 0; i < a.length; i++) {
                  var val = a[i];
                  var type = types[whatis(val)];
                  if(type === 3) {
                      val = orderedStringify(val, fn);
                  } else if(type === 2) {
                      val = arrayStringify(val);
                  } else if(type === 1) {
                      val = '"'+val+'"';
                  }
          
                  if(type !== 4)
                      res += ''+ val+',';
              }
          
              return res.substring(res, res.lastIndexOf(','))+']';
          }
          

          【讨论】:

            【解决方案12】:

            这是一个建议:

            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 中使用引用(所以两个空对象在哈希表中看起来是一样的),因为你没有办法得到它。有关详细信息,请参阅此答案:How to get javascript object references or reference count?

            Jsfiddle 演示:http://jsfiddle.net/HKz3e/

            但是,对于事物的独特之处,您可以像这样玩原始对象:

            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 ];
                }
            };
            

            Jsfiddle 演示:http://jsfiddle.net/HKz3e/2/

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

            这也意味着您可以在此哈希表中存储的项目数量受到限制。仅限于253,即。

            现在,“它不会在任何地方工作”的解决方案:使用 ES6 WeakMaps。它们正是为此目的而完成的:将对象作为键。我建议您阅读 MDN 了解更多信息:https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/WeakMap

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

            var myMap = new WeakMap(),
                object1 = {},
                object2 = {};
            
            myMap.set( object1, 'value1' );
            myMap.set( object2, 'value2' );
            
            console.log( myMap.get( object1 ) ); // "value1"
            console.log( myMap.get( object2 ) ); // "value2"
            

            带有弱映射垫片的 Jsfiddle 演示:http://jsfiddle.net/Ralt/HKz3e/9/

            但是,弱映射在 FF 和 Chrome 中实现(如果您在 chrome 中启用“实验性 javascript 功能”标志)。有可用的垫片,例如:https://gist.github.com/1269991。使用风险自负。

            您也可以使用Maps,它们可能更适合您的需求,因为您还需要将原始值(字符串)存储为键。 Doc, Shim.

            【讨论】:

            • 这确实是一个解决方案,我只是想知道 JSON.stringify 有多重
            • @Babibu,这取决于对象的大小。它越大,字符串化所需的时间就越长。如果将对象存储在一个简单的数组中对您来说太过分了,那么对您的数千个对象进行字符串化可能也是如此。
            • JSON.stringify 很便宜。真的。这不会成为您的应用程序的瓶颈。
            • 这是错误。您不能依赖 JSON.stringify() 中键的顺序。对于具有相同键和值的不同对象,不能保证使用 JSON.stringify() 返回相同的字符串(即使它们“大部分时间”都这样做)。请参阅 javascript - Is there a deterministic equivalent of JSON.stringify? 以获得更好的(但不是简单的 JSON.stringify())对象字符串。
            • @FlorianMargaine 我没有在那个问题的任何地方读到它应该在同一个实现上是确定性的。不过,情况似乎如此。但是我确实读到MDN's stringify page 说“不保证非数组对象的属性以任何特定顺序进行字符串化。不要依赖字符串化中同一对象内的属性排序。”即使它今天有效,也不能保证明天。
            【解决方案13】:

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

            var objects = [];
            objects.push(object1);
            objects.push(object2);
            
            objects[0] === object1; // true
            objects[1] === object1; // false
            

            实现将取决于您如何将对象存储在 HashTable 类中。

            【讨论】:

            • 这不是哈希表,这是效率为 O(n) 的数组列表的坏做法。这不是我想要的
            • JavaScript 中没有内置的哈希表,因此,如果您不想将对象转换为字符串,则需要使用数组并循环遍历它。您的表将存储多少数据?
            • 数以千计,我需要以某种方式找到解决方案。转换为字符串不会向我保证唯一的键。
            猜你喜欢
            • 2018-10-29
            • 2012-04-26
            • 2016-07-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-04-22
            • 2015-05-11
            • 2020-12-23
            相关资源
            最近更新 更多