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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 16:24:46  来源:igfitidea点击:

Difference and intersection of two arrays containing objects

javascriptarraysset-intersectionset-differenceset-operations

提问by Shashi

I have two arrays list1and list2which have objects with some properties; userIdis the Id or unique property:

我有两个数组list1list2其中包含具有某些属性的对象;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:

我正在寻找一种简单的方法来执行以下三个操作:

  1. list1 operation list2should return the intersection of elements:

    [
        { userId: 1235, userName: 'ABC'  },
        { userId: 1236, userName: 'IJKL' }
    ]
    
  2. list1 operation list2should return the list of all elements from list1which don't occur in list2:

    [
        { userId: 1234, userName: 'XYZ'  },
        { userId: 1237, userName: 'WXYZ' }, 
        { userId: 1238, userName: 'LMNO' }
    ]
    
  3. list2 operation list1should return the list of elements from list2which don't occur in list1:

    [
        { userId: 1252, userName: 'AAAA' }
    ]
    
  1. list1 operation list2应该返回元素的交集:

    [
        { userId: 1235, userName: 'ABC'  },
        { userId: 1236, userName: 'IJKL' }
    ]
    
  2. list1 operation list2应该返回list1不出现在的所有元素的列表list2

    [
        { userId: 1234, userName: 'XYZ'  },
        { userId: 1237, userName: 'WXYZ' }, 
        { userId: 1238, userName: 'LMNO' }
    ]
    
  3. 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 inSecondOnlywhich 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 operationthat all three rely on.

您可以定义三个函数inBoth, inFirstOnly, 并且inSecondOnly它们都以两个列表作为参数,并从函数名称中可以理解返回一个列表。主要逻辑可以放在operation三个都依赖的公共函数中。

Here are a few implementations for that operationto choose from, for which you can find a snippet further down:

以下是一些operation可供选择的实现,您可以在下面找到一个片段:

  • Plain old JavaScript forloops
  • Arrow functions using filterand somearray methods
  • Optimised lookup with a Set
  • 普通的旧 JavaScriptfor循环
  • 使用filtersome数组方法的箭头函数
  • 优化查找 Set

Plain old forloops

普通的旧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 filterand somearray methods

使用filtersome数组方法的箭头函数

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 -- somerepresents 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 userIdvalue,
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_.isEqualmethod. 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 filterand somearray methods of JS and you can do that.

只需使用filtersomeJS的阵列的方法和你能做到这一点。

let arr1 = list1.filter(e => {
   return !list2.some(item => item.userId === e.userId);
});

This will return the items that are present in list1but 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 ;-)

我会让你改进这个来回答其他问题;-)