Search code examples
javascriptarraysjsonobjectlodash

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.

Here this abcdef: {name: 'Robin', uid: '123'} is common among the three(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'}

UPDATE: If there is nothing in common, return false. And two or more in common, return all of them.


Solution

  • You can easily do that by using intersectionWith - it accepts a custom comparator for your elements. The easiest way is to convert the object into an array of entries using toPairs and then compare those with 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

    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/[email protected]/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/[email protected]/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. It's the easiest way to do that.

    Alternatively, if you prefer a more FP approach then it can look like this:

    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>