简体   繁体   English

对象数组中的Javascript / Lodash深度比较对象

[英]Javascript / Lodash deep comparison object from an array of object

Suppose i have an array of objects like this 假设我有一个这样的对象数组

let arr = [
  {
    abcdef: {name: 'Robin', uid: '123'},
    ghijkl: {name: 'Simon', uid: '456'}
  },
  {
    mnopqr: {name: 'Alex', uid: '789'},
    abcdef: {name: 'Robin', uid: '123'}
  },
  {
    abcdef: {name: 'Robin', uid: '123'},
    stuvwx: {name: 'Julianna', uid: '111'},
    yzxwuv: {name: 'Elon', uid: '007'}
  }
];

In position of arr[0] , arr[1] and arr[2] , i define a object and inside that object, i define couple of objects. arr[0]arr[1]arr[2] ,我定义了一个对象,在该对象内部,我定义了几个对象。

Here this abcdef: {name: 'Robin', uid: '123'} is common among the three(arr[0], arr[1], arr[2]). 这里的abcdef: {name: 'Robin', uid: '123'}在这三个(arr [0],arr [1],arr [2])中很常见。 So i need to write a function that returns the common one. 所以我需要编写一个返回通用函数的函数。 In this case abcdef: {name: 'Robin', uid: '123'} 在这种情况下, abcdef: {name: 'Robin', uid: '123'}

UPDATE: If there is nothing in common, return false. 更新:如果没有什么共同之处,则返回false。 And two or more in common, return all of them. 并且有两个或更多个共同点,将它们全部归还。

You can easily do that by using intersectionWith - it accepts a custom comparator for your elements. 您可以通过使用intersectionWith轻松地做到这一点-它接受元素的自定义比较器。 The easiest way is to convert the object into an array of entries using toPairs and then compare those with isEqual . 最简单的方法是使用toPairs将对象转换为条目数组,然后将其与isEqual进行比较。 The intersection would then be an array containing pairs of attribute and value, so you can then convert it back to an object using fromPairs 该交集将是一个包含成对的属性和值的数组,因此您可以使用fromPairs将其转换回对象

 let arr = [ { abcdef: {name: 'Robin', uid: '123'}, ghijkl: {name: 'Simon', uid: '456'} }, { mnopqr: {name: 'Alex', uid: '789'}, abcdef: {name: 'Robin', uid: '123'} }, { abcdef: {name: 'Robin', uid: '123'}, stuvwx: {name: 'Julianna', uid: '111'}, yzxwuv: {name: 'Elon', uid: '007'} } ]; const inputPairs = arr.map(_.toPairs); const resultPairs = _.intersectionWith(...inputPairs, _.isEqual); const resultObject = _.fromPairs(resultPairs); console.log(resultObject); 
 <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script> 

Using chaining this can be written as: 使用链接可以写成:

 let arr = [ { abcdef: {name: 'Robin', uid: '123'}, ghijkl: {name: 'Simon', uid: '456'} }, { mnopqr: {name: 'Alex', uid: '789'}, abcdef: {name: 'Robin', uid: '123'} }, { abcdef: {name: 'Robin', uid: '123'}, stuvwx: {name: 'Julianna', uid: '111'}, yzxwuv: {name: 'Elon', uid: '007'} } ]; const resultObject = _(arr) .map(_.toPairs) .thru(pairs => _.intersectionWith(...pairs, _.isEqual)) .fromPairs() .value(); console.log(resultObject); 
 <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script> 

Using thru here because the chain contains an array with other array in it and each needs to be passed as a separate argument to intersectionWith . 在这里使用thru是因为链中包含一个数组,该数组中还有其他数组,并且每个数组都需要作为一个单独的参数传递给intersectionWith It's the easiest way to do that. 这是最简单的方法。

Alternatively, if you prefer a more FP approach then it can look like this: 或者,如果您更喜欢使用FP方法,则可以如下所示:

 const {spread, intersectionWith, isEqual, flow, map, toPairs, fromPairs} = _; let arr = [ { abcdef: {name: 'Robin', uid: '123'}, ghijkl: {name: 'Simon', uid: '456'} }, { mnopqr: {name: 'Alex', uid: '789'}, abcdef: {name: 'Robin', uid: '123'} }, { abcdef: {name: 'Robin', uid: '123'}, stuvwx: {name: 'Julianna', uid: '111'}, yzxwuv: {name: 'Elon', uid: '007'} } ]; const process = flow( map(toPairs), spread(intersectionWith(isEqual)), fromPairs ); const resultObject = process(arr); console.log(resultObject); 
 <script src="https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)"></script> 

Solution

Here's a quick solution I was able to whip up, and I am doing this without lodash intentionally just to illustrate how to do it natively 这是我能够快速完成的快速解决方案,我在没有lodash情况lodash故意这样做,只是为了说明如何在本地进行操作

  const arr = [ { abcdef: { name: 'Robin', uid: '123' }, ghijkl: { name: 'Simon', uid: '456' }, }, { mnopqr: { name: 'Alex', uid: '789' }, abcdef: { name: 'Robin', uid: '123' }, }, { abcdef: { name: 'Robin', uid: '123' }, stuvwx: { name: 'Julianna', uid: '111' }, yzxwuv: { name: 'Elon', uid: '007' }, }, ]; function isEqual(a, b) { const aProps = Object.getOwnPropertyNames(a); const bProps = Object.getOwnPropertyNames(b); if (aProps.length !== bProps.length) { return false; } for (const prop of aProps) { if (a[prop] !== b[prop]) { return false; } } return true; } function findIntersection() { const results = []; const objCount = {}; arr.forEach(element => { Object.entries(element).forEach(entry => { const [key, value] = entry; if (!objCount[key]) { objCount[key] = {}; objCount[key].value = value; objCount[key].count = 1; } else if (isEqual(value, objCount[key].value)) { objCount[key].count += 1; } }); }); Object.keys(objCount).forEach(key => { if (objCount[key].count === arr.length) { results.push({ [key]: objCount[key].value }); } }); if (results.length > 0) { return results; } return false; } const results = findIntersection(arr); console.log('Results', results); 

Explanation 说明

The logic is as follows, 逻辑如下:

  • Iterate over each array element and in turn over all the objects and keep a count of how many times they occur. 遍历每个数组元素,然后遍历所有对象,并计数它们发生的次数。 Only increase the count if the key and value are both identical 仅当键和值相同时才增加计数
  • After getting the count for all the objects, iterate over each object's count value and check if it's equal to the number of elements in the array thus showing the object is common for all elements in the array 获取所有对象的计数后,遍历每个对象的计数值,并检查其是否等于数组中元素的数量,从而显示该对象对于数组中的所有元素都是公共的
  • For each object that satisfies the above condition push it to results array and return it 对于满足上述条件的每个对象,将其推入结果数组并返回

Hope this helps. 希望这可以帮助。 Let me know if it works for you. 请让我知道这对你有没有用。

Just for your reference: 仅供您参考:

let arr = [
  {
    abcdef: {name: 'Robin', uid: '123'},
    ghijkl: {name: 'Simon', uid: '456'}
  },
  {
    mnopqr: {name: 'Alex', uid: '789'},
    abcdef: {name: 'Robin', uid: '123'}
  },
  {
    abcdef: {name: 'Robin', uid: '123'},
    stuvwx: {name: 'Julianna', uid: '111'},
    yzxwuv: {name: 'Elon', uid: '007'}
  }
];
let counterMap = {}, 
names=[], //names collect the keys only which exists in all item, here the value should be ['abcdef']
commons=[];
for (let itm of arr) {
  for (let key of Object.keys(itm)) {
    if (!counterMap[key]) {
      counterMap[key] = 1;
    } else {
      counterMap[key] = counterMap[key] + 1;
    }
  }
}
_.forOwn(counterMap, (val, key) => {
  if (val === arr.length) {
    names.push(key);
  }
})
names.forEach(name => {
  if (_.every(arr, itm => {
    return _.isEqual(itm[name], arr[0][name]);
  })){
    let result = {};
    result[name] = arr[0][name];
    commons.push(result)
  }
})
console.log(commons);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM