【问题标题】:Javascript / Lodash deep comparison object from an array of object来自对象数组的 Javascript / Lodash 深度比较对象
【发布时间】:2019-07-25 06:30:37
【问题描述】:

假设我有一个这样的对象数组

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'}
  }
];

arr[0]arr[1]arr[2] 的位置,我定义了一个对象,在该对象内部,我定义了几个对象。

这里这个abcdef: {name: 'Robin', uid: '123'}在这三个(arr[0]、arr[1]、arr[2])中很常见。所以我需要编写一个返回通用函数的函数。 在这种情况下abcdef: {name: 'Robin', uid: '123'}

更新: 如果没有共同点,则返回 false。并且有两个或更多的共同点,全部返回。

【问题讨论】:

  • 如果有两个共同点会返回什么?
  • 请添加您目前尝试过的代码
  • @huanfeng 如果有两个或更多相同,则全部返回。
  • 那么,您想要创建对象属性的交集吗?
  • @VLAZ 确切地说,有没有使用数学联合或交集的方法?

标签: javascript arrays json object lodash


【解决方案1】:

您可以使用intersectionWith 轻松做到这一点 - 它接受您元素的自定义比较器。最简单的方法是使用toPairs 将对象转换为条目数组,然后将它们与isEqual 进行比较。然后,交集将是一个包含属性和值对的数组,因此您可以使用 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>

使用chaining可以写成:

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>

在这里使用thru,因为链包含一个数组,其中包含其他数组,每个数组都需要作为单独的参数传递给intersectionWith。这是最简单的方法。

或者,如果您更喜欢FP approach,那么它可以如下所示:

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>

【讨论】:

    【解决方案2】:

    仅供参考:

    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);
    

    【讨论】:

    • 这里唯一相关的 Lodash 用法是 _.isEqual有点 _.forOwn。然而,后者可以与for..ofObject.entries 或者甚至只是Object.entries(x).forEach() 轻松交换,几乎没有问题。并且_.every 可以替换为Array#every 而不会丢失功能。
    【解决方案3】:

    解决方案

    这是我能够快速解决的一个快速解决方案,我在没有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);

    说明

    逻辑如下,

    • 遍历每个数组元素并依次遍历所有对象并记录它们出现的次数。仅当键和值都相同时才增加计数
    • 获取所有对象的计数后,遍历每个对象的计数值并检查它是否等于数组中的元素数 因此显示对象对于数组中的所有元素都是通用的
    • 对于满足上述条件的每个对象,将其推送到结果数组并返回

    希望这会有所帮助。让我知道它是否适合你。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-01
      • 1970-01-01
      • 2012-10-20
      • 2015-10-19
      • 1970-01-01
      • 2021-10-10
      • 1970-01-01
      相关资源
      最近更新 更多