检查重复的Javascript对象

检查重复的Javascript对象,javascript,duplicates,Javascript,Duplicates,TL;DR version:我希望避免将重复的Javascript对象添加到类似对象的数组中,其中一些对象可能非常大。最好的方法是什么? 我有一个将大量JSON数据加载到Javascript数据结构中的应用程序。虽然比这要复杂一点,但假设我通过一系列AJAX请求将JSON从服务器加载到Javascript对象数组中,比如: var myObjects = []; function processObject(o) { myObjects.push(o); } for (var x=0

TL;DR version:我希望避免将重复的Javascript对象添加到类似对象的数组中,其中一些对象可能非常大。最好的方法是什么?

我有一个将大量JSON数据加载到Javascript数据结构中的应用程序。虽然比这要复杂一点,但假设我通过一系列AJAX请求将JSON从服务器加载到Javascript对象数组中,比如:

var myObjects = [];

function processObject(o) {
    myObjects.push(o);
}

for (var x=0; x<1000; x++) {
    $.getJSON('/new_object.json', processObject);
}
但是我担心在
myHashMap
中有可能是200 kb长的属性名。因此,我的问题是:

  • 对于这个问题,有没有比hashmap更好的方法
  • 如果没有,有没有比
    JSON.stringify
    更好的方法为任意长度和模式的JSON对象生成哈希函数
  • 对象中超长属性名可能存在哪些问题

我建议您创建JSON.stringify(o)的MD5散列,并将其存储在hashmap中,并引用存储的对象作为散列的数据。为了确保
JSON.stringify()
中没有对象键顺序的差异,您必须创建一个对键进行排序的对象副本

然后,当每个新对象进入时,您将对照哈希映射检查它。如果在哈希映射中找到匹配项,则将传入对象与存储的实际对象进行比较,以查看它们是否真正重复(因为可能存在MD5哈希冲突)。这样,您就有了一个可管理的哈希表(其中只有MD5哈希)

下面是创建对象(包括嵌套对象或数组中的对象)的规范字符串表示的代码,该对象处理的对象键的顺序可能与调用JSON.stringify()不同

//执行规范化JSON.stringify()的代码,该函数用于放置对象属性
//以一致的顺序
//不允许循环引用(包含对父级引用的子级)
JSON.stringifyCanonical=函数(obj){
//与browser或node.js兼容
var Set=typeof window==“object”?window.Set:global.Set;
//可怜的男人
if(字体设置!=“功能”){
集合=功能{
若有(s){
this.data=s.data.slice();
}否则{
这个.data=[];
}
};
Set.prototype={
添加:功能(项目){
此.data.push(项目);
},
has:功能(项目){
返回此.data.indexOf(项)!=-1;
}
};
}
函数顺序键(对象、父对象){
if(对象类型!=“对象”){
抛出新错误(“orderKeys()需要对象类型”);
}
var集合=新集合(父集合);
如果(设置有(obj)){
抛出新错误(“stringifyCanonical()中的循环对象”);
}
集合。添加(obj);
var tempObj,项目,i;
if(数组isArray(obj)){
//无需重新订购阵列
//但是需要检查它是否有需要排序的嵌入对象
tempObj=[];
对于(i=0;i
算法

var myHashMap = {};

function processObject(o) {
    var stringifiedCandidate = JSON.stringifyCanonical(o);
    var hash = CreateMD5(stringifiedCandidate);
    var list = [], found = false;
    // is it in the hashmap?
    if (!myHashMap[hash] {
        // not in the hash table, so it's a unique object
        myObjects.push(o);
        list.push(myObjects.length - 1);    // put a reference to the object with this hash value in the list
        myHashMap[hash] = list;             // store the list in the hash table for future comparisons
    } else {
        // the hash does exist in the hash table, check for an exact object match to see if it's really a duplicate
        list = myHashMap[hash];             // get the list of other object indexes with this hash value
        // loop through the list
        for (var i = 0; i < list.length; i++) {
            if (stringifiedCandidate === JSON.stringifyCanonical(myObjects[list[i]])) {
                found = true;       // found an exact object match
                break;
            }
        }
        // if not found, it's not an exact duplicate, even though there was a hash match
        if (!found) {
            myObjects.push(o);
            myHashMap[hash].push(myObjects.length - 1);
        }
    }
}
var myHashMap={};
函数processObject(o){
var stringifiedCandidate=JSON.stringificanonical(o);
var hash=CreateMD5(stringifiedCandidate);
var list=[],found=false;
//它在hashmap中吗?
如果(!myHashMap[hash]{
//不在哈希表中,因此它是唯一的对象
肌对象推(o);
list.push(myObjects.length-1);//在列表中放置对具有此哈希值的对象的引用
myHashMap[hash]=list;//将列表存储在哈希表中以备将来比较
}否则{
//哈希表中确实存在哈希,请检查是否存在精确的对象匹配,以查看它是否真的是重复的
list=myHashMap[hash];//使用此哈希值获取其他对象索引的列表
//循环浏览列表
对于(变量i=0;i
jsonStringifyCanonical()
的测试用例如下:

  • 也许吧。例如,如果你知道对象是什么类型的,你可以编写比JS对象键更好的索引和搜索系统。但你只能用JavaScript编写,而对象键是用C
  • 你的散列必须是无损的吗?如果你能尝试丢失压缩(MD5)。我猜你会损失一些速度并获得一些内存。顺便说一句,使用JSON。stringify(o)保证相同的键顺序。因为
    {foo:1,bar:2}
    {bar:2,foo:1}
    作为对象而不是字符串是相等的
  • 成本存储器
  • 一种可能的优化:

    不要使用
    getJSON
    而是使用
    $。
    
    // Code to do a canonical JSON.stringify() that puts object properties 
    // in a consistent order
    // Does not allow circular references (child containing reference to parent)
    JSON.stringifyCanonical = function(obj) {
        // compatible with either browser or node.js
        var Set = typeof window === "object" ? window.Set : global.Set;
    
        // poor man's Set polyfill
        if (typeof Set !== "function") {
            Set = function(s) {
                if (s) {
                    this.data = s.data.slice();
                } else {
                    this.data = [];
                }
            };
            Set.prototype = {
                add: function(item) {
                    this.data.push(item);
                },
                has: function(item) {
                    return this.data.indexOf(item) !== -1;
                }
            };
        }
    
        function orderKeys(obj, parents) {
            if (typeof obj !== "object") {
                throw new Error("orderKeys() expects object type");
            }
            var set = new Set(parents);
            if (set.has(obj)) {
                throw new Error("circular object in stringifyCanonical()");
            }
            set.add(obj);
            var tempObj, item, i;
            if (Array.isArray(obj)) {
                // no need to re-order an array
                // but need to check it for embedded objects that need to be ordered
                tempObj = [];
                for (i = 0; i < obj.length; i++) {
                    item = obj[i];
                    if (typeof item === "object") {
                        tempObj[i] = orderKeys(item, set);
                    } else {
                        tempObj[i] = item;
                    }
                }
            } else {
                tempObj = {};
                // get keys, sort them and build new object
                Object.keys(obj).sort().forEach(function(item) {
                    if (typeof obj[item] === "object") {
                        tempObj[item] = orderKeys(obj[item], set);
                    } else {
                        tempObj[item] = obj[item];
                    }
                });
            }
            return tempObj;
        }
    
        return JSON.stringify(orderKeys(obj));
    }
    
    var myHashMap = {};
    
    function processObject(o) {
        var stringifiedCandidate = JSON.stringifyCanonical(o);
        var hash = CreateMD5(stringifiedCandidate);
        var list = [], found = false;
        // is it in the hashmap?
        if (!myHashMap[hash] {
            // not in the hash table, so it's a unique object
            myObjects.push(o);
            list.push(myObjects.length - 1);    // put a reference to the object with this hash value in the list
            myHashMap[hash] = list;             // store the list in the hash table for future comparisons
        } else {
            // the hash does exist in the hash table, check for an exact object match to see if it's really a duplicate
            list = myHashMap[hash];             // get the list of other object indexes with this hash value
            // loop through the list
            for (var i = 0; i < list.length; i++) {
                if (stringifiedCandidate === JSON.stringifyCanonical(myObjects[list[i]])) {
                    found = true;       // found an exact object match
                    break;
                }
            }
            // if not found, it's not an exact duplicate, even though there was a hash match
            if (!found) {
                myObjects.push(o);
                myHashMap[hash].push(myObjects.length - 1);
            }
        }
    }