JavaScript 中通过公共属性“交叉”两个 JSON 对象数组的最佳方式

Best way in JavaScript to "intersect" two arrays of JSON objects by common property?

本文关键字:对象 JSON 两个 数组 方式 最佳 交叉 属性 JavaScript      更新时间:2023-09-26

假设我们有两个数组,arr1arr2 ,由 JSON 对象组成,它们共享一个公共属性id

var arr1 = [{'id':10,'value1':'a1'},
            {'id':11,'value1':'b1'},
            {'id':12,'value1':'c1'},
            {'id':13,'value1':'d1'},
            {'id':14,'value1':'e1'}];
var arr2 = [{'id':100000,'value2':'a2'},
            {'id':11,'value2':'b2'},
            {'id':100002,'value2':'c2'},
            {'id':100003,'value2':'d2'},
            {'id':14,'value2':'e2'}];

我需要从 ID 匹配的两个数组中提取"值"项,即预期的输出将是:

[0] value1: b1, value2: b2
[1] value1: e1, value2: e2

我有一个嵌套循环解决方案的工作示例(见下文),但它很慢(想象一下这些数组很大,每次迭代都涉及一些 UI 操作),所以我真的希望有一个更优雅、更快速的解决方案。

var arr1 = [{'id':10,'value1':'a1'},
            {'id':11,'value1':'b1'},
            {'id':12,'value1':'c1'},
            {'id':13,'value1':'d1'},
            {'id':14,'value1':'e1'}];
var arr2 = [{'id':100000,'value2':'a2'},
            {'id':11,'value2':'b2'},
            {'id':100002,'value2':'c2'},
            {'id':100003,'value2':'d2'},
            {'id':14,'value2':'e2'}];
for (var i = 0, len1 = arr1.length; i < len1; i++) {
	for (var j = 0, len2 = arr2.length; j < len2; j++) {
		if (arr1[i].id === arr2[j].id) {
			theFunction(arr1[i].value1, arr2[j].value2);
			break;
		}
	}
}
function theFunction(value1, value2) {
  var div = document.getElementById('out');
  div.innerHTML += '<br />value1: ' + value1 + ', value2: ' + value2;
}
<div id='out'>
</div>

构建要value1id映射,然后迭代arr2匹配它们:

var arr1 = [{'id':10,'value1':'a1'},
            {'id':11,'value1':'b1'},
            {'id':12,'value1':'c1'},
            {'id':13,'value1':'d1'},
            {'id':14,'value1':'e1'}];
var arr2 = [{'id':100000,'value2':'a2'},
            {'id':11,'value2':'b2'},
            {'id':100002,'value2':'c2'},
            {'id':100003,'value2':'d2'},
            {'id':14,'value2':'e2'}];
var value1s = {};
arr1.forEach(function(item) {
  value1s[item.id] = item.value1;
});
arr2.forEach(function(item) {
  if (item.id in value1s) {
    theFunction(value1s[item.id], item.value2);
  }
});
function theFunction(value1, value2) {
  var div = document.getElementById('out');
  div.innerHTML += '<br />value1: ' + value1 + ', value2: ' + value2;
}
<div id='out'>
</div>

您可以先构建简单的 id 数组,然后更简单地使用它们,如下所示:

var arr1 = [{'id':10,'value1':'a1'},
            {'id':11,'value1':'b1'},
            {'id':12,'value1':'c1'},
            {'id':13,'value1':'d1'},
            {'id':14,'value1':'e1'}];
var arr2 = [{'id':100000,'value2':'a2'},
            {'id':11,'value2':'b2'},
            {'id':100002,'value2':'c2'},
            {'id':100003,'value2':'d2'},
            {'id':14,'value2':'e2'}];
var tmp1 = arr1.map(function(obj) {
  return obj.id;
});
var tmp2 = arr2.map(function(obj) {
  return obj.id;
});
tmp1.forEach(function(id1, index1) {
  var index2 = tmp2.indexOf(id1);
  if (index2 > -1) {
    document.getElementById('out').innerHTML +=
      '<br />value1: ' + arr1[index1].value1 + 
      ', value2: ' + arr2[index2].value2;
  }
});
<div id='out'>
</div>

我会提取id并将其用作对象的键。这样比较两者之间的存在将快得多。

var id1 = arr1.reduce(function(p,v,i){p[v.id] = i; return p;}, {}),
    id2 = arr2.reduce(function(p,v,i){p[v.id] = i; return p;}, {});
for(key in id1){
   if (id2.hasOwnProperty(key)){
        var element1 = arr1[id1[key]],
            element2 = arr2[id2[key]];
        theFunction(element1.value1, element2 .value2);
   }
}

https://jsfiddle.net/gaby/1ej0jxpq/2/演示

我认为循环中最慢的部分将是DOM操作。如果你把它移到循环之外,只是在循环中构建一个字符串,它应该会快得多......

var arr1 = [{'id':10,'value1':'a1'},
            {'id':11,'value1':'b1'},
            {'id':12,'value1':'c1'},
            {'id':13,'value1':'d1'},
            {'id':14,'value1':'e1'}];
var arr2 = [{'id':100000,'value2':'a2'},
            {'id':11,'value2':'b2'},
            {'id':100002,'value2':'c2'},
            {'id':100003,'value2':'d2'},
            {'id':14,'value2':'e2'}];
// create a string to build results
var out = '';
for (var i = 0, len1 = arr1.length; i < len1; i++) {
	for (var j = 0, len2 = arr2.length; j < len2; j++) {
		if (arr1[i].id === arr2[j].id) {
            // build results string in loop
			out += '<br />value1: ' + arr1[i].value1 + ', value2: ' + arr2[j].value2;
			break;
		}
	}
}
// append results to dom
document.getElementById('out').innerHTML += out;
<div id='out'>
</div>

假设顺序不重要,您可以采用不同的方法,通过连接、排序,然后在单个循环中比较相邻的值。

var arr1 = [{'id':10,'value1':'a1'},
            {'id':11,'value1':'b1'},
            {'id':12,'value1':'c1'},
            {'id':13,'value1':'d1'},
            {'id':14,'value1':'e1'}];
var arr2 = [{'id':100000,'value2':'a2'},
            {'id':11,'value2':'b2'},
            {'id':100002,'value2':'c2'},
            {'id':100003,'value2':'d2'},
            {'id':14,'value2':'e2'}];
// join the two arrays, and sort them so you can loop through
// once comparing current to last value
arr1.concat(arr2).sort(function(a,b){
    return a.id !== b.id;
}).reduce(function(last, item){
    if(item.id === last.id){ theFunction(last.value1, item.value2) }
    return item;
}, {});
function theFunction(value1, value2) {
  var div = document.getElementById('out');
  div.innerHTML += '<br />value1: ' + value1 + ', value2: ' + value2;
}
<div id='out'>
</div>

我认为这是一样的,但是一个更优雅的解决方案:

arr1.forEach(function(e,i) {
    var same = arr2.filter(function(o, p) {
        var eq = o.id === e.id;
        if(eq) { arr2.splice(p, 1); } // remove used elements
        return eq;
    });
    // here 'same' is an array of objects with the same id, 
    // we don't know how big it is, but
    // at least the first position always has something        
    if(same.length) {
        console.log(e, ", ", same[0])
    }
})