如何使用Javascript获取包含重复值的数组交集

4
我需要类似于lodash.intersectionWith的东西,但是我还需要在结果数组中包含重复的值。
例如:
var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
_.intersectionWith(objects, others, _.isEqual);

预期结果:

[{ 'x': 1, 'y': 2 },{ 'x': 1, 'y': 2 }]

感谢提前!

不太清楚您为什么期望那个对象有两个副本而不是三个。 - Omri Aharon
因为数组#1有2个值与数组#2匹配,所以我猜测... - Randy
3个回答

3

通过过滤掉第一个数组中与第二个数组不匹配的项,可以找到交集。第一个数组中的任何重复项都将被保留。

var intersectwith = function(f,xs,ys){
    return xs.filter(function(x){
        return ys.some(function(y){
            return f(x,y);
        });
    });
};

var equals = function(x,y){
    return x === y;
};
console.log(intersectwith(equals, [1,2,3], [1,1,2,2,4]));
console.log(intersectwith(equals, [1,1,2,2,4], [1,2,3]));

或者,更易读的方式,使用ES6:

const intersectwith = (f,xs,ys) => xs.filter(x => ys.some(y => f(x,y)));
const equals = (x,y) => x === y;

console.log(intersectwith(equals, [1,2,3], [1,1,2,2,4]));
console.log(intersectwith(equals, [1,1,2,2,4], [1,2,3]));

使用_.isEqual代替equals来比较对象:jsfiddle

有用的文档:
Array.prototype.filter
Array.prototype.some


你可以使用不同的比较函数,例如 _.isEqual。我想在代码片段中展示它的工作原理。 - 1983
修改后,添加了 jsfiddle 示例展示它如何在对象中运行。 - 1983

3
您可以使用 differenceWith() 来获取源 object 和源 objectothers 对象的对称差异之间的差异,使用 xorWith()
var result = _.differenceWith(
  objects, 
  _.xorWith(objects, others, _.isEqual), 
  _.isEqual
);

var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];


var intersection = _.intersectionWith(objects, others, _.isEqual);

var result = _.differenceWith(
  objects, 
  _.xorWith(objects, others, _.isEqual), 
  _.isEqual
);

document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.js"></script>


我喜欢这种方法,但是有一个问题,我的实际数组具有不同的对象类型,“对象是Jquery”和“其他是javascript对象”。问题在于xorWith()的比较器函数在某些时候会切换参数(不知道为什么)。 - Mikhail
@Mikhail 请检查我的更新,我将difference()更改为differenceWith() - ryeballar
intersectionWith() 运行良好。我制作了一个 测试,你可以去看看。 - Mikhail
我看到在你的test中,源object仅包含xy属性。你应该添加这样的语句。而且你的测试肯定失败了。如果你记录交集result,它会返回一个空数组。 - ryeballar
我更新了demo。但无论如何,这只是为了看到Xor和Intersection比较函数之间的区别。 - Mikhail
此外,lodash pullAllWith 可以删除所有重复值,并且可以用作解决方法,如果您不关心“对象”数组。这里是演示:demo - Mikhail

0
使用reduce函数验证第一个array中的每个object,然后检查该object是否存在于第二个array中。如果存在,则reduce将把该object推入其array中。 reduce函数将自动返回该新array

var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];

var res = objects.reduce(
  function(arr, obj){
    if(containsObject(obj, others))
      arr.push(obj);
    return arr;
  }, 
  []
);


function containsObject(obj, list) {
    var x;
    var ret = false;
  
    list.forEach(function(s){
      ret = JSON.stringify(s) == JSON.stringify(obj);
    });

    return ret;
}

console.log(res);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接