[英]Difference and intersection of two arrays containing objects
我有两个数组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' }
]
我正在寻找一种简单的方法来执行以下三个操作:
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' } ]
您可以定义三种功能inBoth
, inFirstOnly
和inSecondOnly
所有需要两个列表作为参数,并返回一个列表,可以从函数名称可以理解。 主要逻辑可以放在三者都依赖的共同功能operation
中。
以下是可供选择的operation
的一些实现,您可以在其中找到更远的代码段:
for
循环 filter
和some
数组方法 Set
优化查找 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));
filter
和some
数组方法 这使用了一些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));
由于嵌套循环,上述解决方案具有O(n²)时间复杂度 - some
代表一个循环。 因此,对于大型数组,您最好在user-id上创建(临时)哈希。 这可以通过提供Set
(ES6)作为将生成过滤器回调函数的函数的参数来即时完成。 然后,该函数就可以在固定时间内查找与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));
使用lodash的 _.isEqual
方法。 特别:
list1.reduce(function(prev, curr){
!list2.some(function(obj){
return _.isEqual(obj, curr)
}) ? prev.push(curr): false;
return prev
}, []);
上面给出了相当于A given !B
(在SQL术语中, A LEFT OUTER JOIN B
)。 您可以在代码周围移动代码以获得您想要的内容!
简答:
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));
更长的答案:
上面的代码将按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)));
还有一种方法可以通过引用比较对象
警告! 具有相同值的两个对象将被视为不同:
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
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]); })
}
这是对我有用的解决方案。
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;
}
这是一个功能强大的编程解决方案,带有下划线/ 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
}, []);
我会让你改进这个以回答其他问题;-)
只需使用JS的filter
和some
数组方法,你就可以做到这一点。
let arr1 = list1.filter(e => {
return !list2.some(item => item.userId === e.userId);
});
这将返回list1
但不在list2
。 如果您要查找两个列表中的常用项目。 就这样做吧。
let arr1 = list1.filter(e => {
return list2.some(item => item.userId === e.userId); // take the ! out and you're done
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.