我需要能够在任意数量的数组之间找到一个公共项。例如,让我们说有一个像这样的对象:
var obj = {
a: [ 15, 23, 36, 49, 104, 211 ],
b: [ 9, 12, 23 ],
c: [ 11, 17, 18, 23, 38 ],
d: [ 13, 21, 23, 27, 40, 85]
};
我需要确定每个阵列之间的公共项目。 (在这种情况下,23)。
我的解决方案是找到最短的数组,并遍历其中的每个项目,检查其他数组的索引。
var shortest = {};
var keys = [];
for ( var key in obj ) {
if ( obj.hasOwnProperty( key ) && Array.isArray( obj[ key ] ) ) {
keys.push( key );
if ( !shortest.hasOwnProperty( 'length' ) || obj[ key ].length < shortest.length ) {
shortest.name = key;
shortest.length = obj[ key ].length;
}
}
}
var res = obj[ shortest.name ].filter(function ( v ) {
for ( var i = 0; i < keys.length; i++ ) {
if ( obj[ keys[ i ] ].indexOf( v ) === -1 ) {
return false;
}
return true;
}
};
然而,这似乎效率极低,而且我试图确定是否有更好的方法,最好不要多次循环。
答案 0 :(得分:2)
我认为不可能在O(N)
以内执行此操作,其中N
是所有数组中的项目数。您当前的解决方案效率较低,因为indexOf
对于每个数组都是O(N)
,并且您可以针对最短数组中的每个项目运行所有这些解决方案。
我认为基于地图的选项为O(N)
:
var counts = {};
var keys = Object.keys(obj);
var arr, el;
for (var k = 0; k < keys.length; k++) {
arr = obj[keys[k]];
for (var i = 0; i < arr.length; i++) {
el = arr[i];
if (counts[el] === undefined) {
// new value, start counting
counts[el] = 1;
} else {
// increment count for this value
counts[el]++;
// if we have as many values as keys, we're done
if (counts[el] === keys.length) {
return el;
}
}
}
}
这里有一些警告:
这假定元素可以用作对象键(即它们可以唯一地转换为字符串),并且您没有像1
和{{1}这样的令人讨厌的边缘情况在不同的数组中。
这假设每个数组的数组值都是唯一的。
这假设交叉点中只有一个元素。
答案 1 :(得分:2)
另一个O(n)
集联合解决方案,在功能上编码得更多。作为对象键警告的元素仍然适用,尽管这将返回交集中所有共享元素的set(数组)。
function common(o) {
// Map each of the object key arrays to a set.
return Object.keys(o).map(function(k) {
return o[k].reduce(function(a, e) {
a[e] = 1;
return a;
}, {});
}).reduce(function(a, e) {
// Perform a set union.
Object.keys(e).forEach(function(k) {
if (!a[k]) {
delete e[k];
}
});
return e;
})
}
var obj = {
a: [ 15, 23, 36, 49, 104, 211 ],
b: [ 9, 12, 23 ],
c: [ 11, 17, 18, 23, 38 ],
d: [ 13, 21, 23, 27, 40, 85]
};
common(obj);
答案 2 :(得分:0)
我会通过Array.prototype.intersect()
的发明来完成这项工作。当阵列中有重复项时,它不会失败。如果在每个数组中有相同的项目重复,您将在交集中得到副本。让我们看看它将如何运作
Array.prototype.intersect = function(...a) {
return [this,...a].reduce((p,c) => p.filter(e => c.includes(e)));
};
var obj = {
a: [ 15, 23, 36, 49, 104, 211 ],
b: [ 9, 12, 23 ],
c: [ 11, 17, 18, 23, 38 ],
d: [ 13, 21, 23, 27, 40, 85]
},
arrs = Object.keys(obj).map(e => obj[e]);
console.log(JSON.stringify(arrs.pop().intersect(...arrs))); // take the last one out to invoke the method over the rest
&#13;
答案 3 :(得分:0)
ES5中的另一个解决方案,在 O(n)中有一个临时对象。
var obj = { a: [15, 23, 36, 49, 104, 211], b: [9, 12, 23, 15], c: [11, 17, 18, 23, 38], d: [13, 21, 23, 27, 40, 85] },
result = Object.keys(obj).reduce(function (o) {
return function (r, k, i) {
var t = o;
o = Object.create(null);
return obj[k].filter(function (a) {
return (!i || t[a]) && (o[a] = true);
});
};
}(Object.create(null)), []);
console.log(result);
&#13;