javascript 包含对象的两个数组的差异和交集
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33356504/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Difference and intersection of two arrays containing objects
提问by Shashi
I have two arrays list1
and list2
which have objects with some properties; userId
is the Id or unique property:
我有两个数组list1
,list2
其中包含具有某些属性的对象;userId
是 Id 或唯一属性:
list1 = [
{ userId: 1234, userName: 'XYZ' },
{ userId: 1235, userName: 'ABC' },
{ userId: 1236, userName: 'IJKL' },
{ userId: 1237, userName: 'WXYZ' },
{ userId: 1238, userName: 'LMNO' }
]
list2 = [
{ userId: 1235, userName: 'ABC' },
{ userId: 1236, userName: 'IJKL' },
{ userId: 1252, userName: 'AAAA' }
]
I'm looking for an easy way to execute the following three operations:
我正在寻找一种简单的方法来执行以下三个操作:
list1 operation list2
should return the intersection of elements:[ { userId: 1235, userName: 'ABC' }, { userId: 1236, userName: 'IJKL' } ]
list1 operation list2
should return the list of all elements fromlist1
which don't occur inlist2
:[ { userId: 1234, userName: 'XYZ' }, { userId: 1237, userName: 'WXYZ' }, { userId: 1238, userName: 'LMNO' } ]
list2 operation list1
should return the list of elements fromlist2
which don't occur inlist1
:[ { userId: 1252, userName: 'AAAA' } ]
list1 operation list2
应该返回元素的交集:[ { userId: 1235, userName: 'ABC' }, { userId: 1236, userName: 'IJKL' } ]
list1 operation list2
应该返回list1
不出现在的所有元素的列表list2
:[ { userId: 1234, userName: 'XYZ' }, { userId: 1237, userName: 'WXYZ' }, { userId: 1238, userName: 'LMNO' } ]
list2 operation list1
应该返回list2
不出现在的元素列表list1
:[ { userId: 1252, userName: 'AAAA' } ]
采纳答案by Shashi
This is the solution that worked for me.
这是对我有用的解决方案。
var intersect = function (arr1, arr2) {
var intersect = [];
_.each(arr1, function (a) {
_.each(arr2, function (b) {
if (compare(a, b))
intersect.push(a);
});
});
return intersect;
};
var unintersect = function (arr1, arr2) {
var unintersect = [];
_.each(arr1, function (a) {
var found = false;
_.each(arr2, function (b) {
if (compare(a, b)) {
found = true;
}
});
if (!found) {
unintersect.push(a);
}
});
return unintersect;
};
function compare(a, b) {
if (a.userId === b.userId)
return true;
else return false;
}
回答by trincot
You could define three functions inBoth
, inFirstOnly
, and inSecondOnly
which all take two lists as arguments, and return a list as can be understood from the function name. The main logic could be put in a common function operation
that all three rely on.
您可以定义三个函数inBoth
, inFirstOnly
, 并且inSecondOnly
它们都以两个列表作为参数,并从函数名称中可以理解返回一个列表。主要逻辑可以放在operation
三个都依赖的公共函数中。
Here are a few implementations for that operation
to choose from, for which you can find a snippet further down:
以下是一些operation
可供选择的实现,您可以在下面找到一个片段:
- Plain old JavaScript
for
loops - Arrow functions using
filter
andsome
array methods - Optimised lookup with a
Set
- 普通的旧 JavaScript
for
循环 - 使用
filter
和some
数组方法的箭头函数 - 优化查找
Set
Plain old for
loops
普通的旧for
循环
// Generic helper function that can be used for the three operations:
function operation(list1, list2, isUnion) {
var result = [];
for (var i = 0; i < list1.length; i++) {
var item1 = list1[i],
found = false;
for (var j = 0; j < list2.length && !found; j++) {
found = item1.userId === list2[j].userId;
}
if (found === !!isUnion) { // isUnion is coerced to boolean
result.push(item1);
}
}
return result;
}
// Following functions are to be used:
function inBoth(list1, list2) {
return operation(list1, list2, true);
}
function inFirstOnly(list1, list2) {
return operation(list1, list2);
}
function inSecondOnly(list1, list2) {
return inFirstOnly(list2, list1);
}
// Sample data
var list1 = [
{ userId: 1234, userName: 'XYZ' },
{ userId: 1235, userName: 'ABC' },
{ userId: 1236, userName: 'IJKL' },
{ userId: 1237, userName: 'WXYZ' },
{ userId: 1238, userName: 'LMNO' }
];
var list2 = [
{ userId: 1235, userName: 'ABC' },
{ userId: 1236, userName: 'IJKL' },
{ userId: 1252, userName: 'AAAA' }
];
console.log('inBoth:', inBoth(list1, list2));
console.log('inFirstOnly:', inFirstOnly(list1, list2));
console.log('inSecondOnly:', inSecondOnly(list1, list2));
Arrow functions using filter
and some
array methods
使用filter
和some
数组方法的箭头函数
This uses some ES5 and ES6 features:
这使用了一些 ES5 和 ES6 特性:
// Generic helper function that can be used for the three operations:
const operation = (list1, list2, isUnion = false) =>
list1.filter( a => isUnion === list2.some( b => a.userId === b.userId ) );
// Following functions are to be used:
const inBoth = (list1, list2) => operation(list1, list2, true),
inFirstOnly = operation,
inSecondOnly = (list1, list2) => inFirstOnly(list2, list1);
// Sample data
const list1 = [
{ userId: 1234, userName: 'XYZ' },
{ userId: 1235, userName: 'ABC' },
{ userId: 1236, userName: 'IJKL' },
{ userId: 1237, userName: 'WXYZ' },
{ userId: 1238, userName: 'LMNO' }
];
const list2 = [
{ userId: 1235, userName: 'ABC' },
{ userId: 1236, userName: 'IJKL' },
{ userId: 1252, userName: 'AAAA' }
];
console.log('inBoth:', inBoth(list1, list2));
console.log('inFirstOnly:', inFirstOnly(list1, list2));
console.log('inSecondOnly:', inSecondOnly(list1, list2));
Optimising lookup
优化查找
The above solutions have a O(n2)time complexity because of the nested loop -- some
represents a loop as well. So for large arrays you'd better create a (temporary) hash on user-id. This can be done on-the-flyby providing a Set
(ES6) as argument to a function that will generate the filter callback function. That function can then perform the look-up in constant time with has
:
由于嵌套循环,上述解决方案的时间复杂度为O(n2)—— 也some
代表一个循环。因此,对于大型数组,您最好在用户 ID 上创建一个(临时)散列。这可以通过将(ES6) 作为参数提供给将生成过滤器回调函数的函数来即时完成Set
。然后该函数可以在恒定时间内执行查找has
:
// Generic helper function that can be used for the three operations:
const operation = (list1, list2, isUnion = false) =>
list1.filter(
(set => a => isUnion === set.has(a.userId))(new Set(list2.map(b => b.userId)))
);
// Following functions are to be used:
const inBoth = (list1, list2) => operation(list1, list2, true),
inFirstOnly = operation,
inSecondOnly = (list1, list2) => inFirstOnly(list2, list1);
// Sample data
const list1 = [
{ userId: 1234, userName: 'XYZ' },
{ userId: 1235, userName: 'ABC' },
{ userId: 1236, userName: 'IJKL' },
{ userId: 1237, userName: 'WXYZ' },
{ userId: 1238, userName: 'LMNO' }
];
const list2 = [
{ userId: 1235, userName: 'ABC' },
{ userId: 1236, userName: 'IJKL' },
{ userId: 1252, userName: 'AAAA' }
];
console.log('inBoth:', inBoth(list1, list2));
console.log('inFirstOnly:', inFirstOnly(list1, list2));
console.log('inSecondOnly:', inSecondOnly(list1, list2));
回答by Trizalio
short answer:
简短的回答:
list1.filter(a => list2.some(b => a.userId === b.userId));
list1.filter(a => !list2.some(b => a.userId === b.userId));
list2.filter(a => !list1.some(b => a.userId === b.userId));
longer answer:
The code above will check objects by userId
value,
if you need complex compare rules, you can define custom comparator:
更长的答案:
上面的代码将按userId
值检查对象,
如果您需要复杂的比较规则,您可以定义自定义比较器:
comparator = function (a, b) {
return a.userId === b.userId && a.userName === b.userName
};
list1.filter(a => list2.some(b => comparator(a, b)));
list1.filter(a => !list2.some(b => comparator(a, b)));
list2.filter(a => !list1.some(b => comparator(a, b)));
Also there is a way to compare objects by references
WARNING!two objects with same values will be considered different:
还有一种方法可以通过引用来比较对象
警告!具有相同值的两个对象将被视为不同:
o1 = {"userId":1};
o2 = {"userId":2};
o1_copy = {"userId":1};
o1_ref = o1;
[o1].filter(a => [o2].includes(a)).length; // 0
[o1].filter(a => [o1_copy].includes(a)).length; // 0
[o1].filter(a => [o1_ref].includes(a)).length; // 1
回答by Bwaxxlo
Use lodash's_.isEqual
method. Specifically:
使用lodash的_.isEqual
方法。具体来说:
list1.reduce(function(prev, curr){
!list2.some(function(obj){
return _.isEqual(obj, curr)
}) ? prev.push(curr): false;
return prev
}, []);
Above gives you the equivalent of A given !B
(in SQL terms, A LEFT OUTER JOIN B
). You can move the code around the code to get what you want!
以上为您提供了等效的A given !B
(在 SQL 术语中,A LEFT OUTER JOIN B
)。您可以在代码周围移动代码以获得您想要的!
回答by Sagi
function intersect(first, second) {
return intersectInternal(first, second, function(e){ return e });
}
function unintersect(first, second){
return intersectInternal(first, second, function(e){ return !e });
}
function intersectInternal(first, second, filter) {
var map = {};
first.forEach(function(user) { map[user.userId] = user; });
return second.filter(function(user){ return filter(map[user.userId]); })
}
回答by Koushik Das
Just use filter
and some
array methods of JS and you can do that.
只需使用filter
和some
JS的阵列的方法和你能做到这一点。
let arr1 = list1.filter(e => {
return !list2.some(item => item.userId === e.userId);
});
This will return the items that are present in list1
but not in list2
. If you are looking for the common items in both lists. Just do this.
这将返回存在于 中list1
但不存在于 中的项目list2
。如果您正在寻找两个列表中的公共项目。就这样做。
let arr1 = list1.filter(e => {
return list2.some(item => item.userId === e.userId); // take the ! out and you're done
});
回答by laruiss
Here is a functionnal programmingsolution with underscore/lodash to answer your first question (intersection).
这是一个带有下划线/lodash的函数式编程解决方案来回答你的第一个问题(交集)。
list1 = [ {userId:1234,userName:'XYZ'},
{userId:1235,userName:'ABC'},
{userId:1236,userName:'IJKL'},
{userId:1237,userName:'WXYZ'},
{userId:1238,userName:'LMNO'}
];
list2 = [ {userId:1235,userName:'ABC'},
{userId:1236,userName:'IJKL'},
{userId:1252,userName:'AAAA'}
];
_.reduce(list1, function (memo, item) {
var same = _.findWhere(list2, item);
if (same && _.keys(same).length === _.keys(item).length) {
memo.push(item);
}
return memo
}, []);
I'll let you improve this to answer the other questions ;-)
我会让你改进这个来回答其他问题;-)