0

I have an array that looks like this:

const myArray = [
  [
    {id: 1, name: 'Liam'},
    {id: 2, name: 'Oliver'},
    {id: 3, name: 'Jake'},
  ],
  [
    {id: 1, name: 'Liam'},
    {id: 2, name: 'Oliver'},
    {id: 4, name: 'Joe'},
  ],
]

I need to find common elements by id, and return them in an array that would look something like this:

[
  {id: 1, name: 'Liam'},
  {id: 2, name: 'Oliver'},
]

If there isn't any way to do it with lodash, just JS could work too. Note that I do not know how many of these arrays I will have inside, so it should work for any number.

4 Answers 4

2

You can use lodash's _.intersectionBy(). You'll need to spread myArray because _intersectionBy() expect arrays as arguments, and not a single array of array:

const myArray = [[{"id":1,"name":"Liam"},{"id":2,"name":"Oliver"},{"id":3,"name":"Jake"}],[{"id":1,"name":"Liam"},{"id":2,"name":"Oliver"},{"id":4,"name":"Joe"}]]

const result = _.intersectionBy(...myArray, 'id')

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js" integrity="sha512-90vH1Z83AJY9DmlWa8WkjkV79yfS2n2Oxhsi2dZbIv0nC4E6m5AbH8Nh156kkM7JePmqD6tcZsfad1ueoaovww==" crossorigin="anonymous"></script>

Sign up to request clarification or add additional context in comments.

2 Comments

I already tried this, but did not spread 'myArray', so it gave me incorrect results. -.- Solved, thank you!
You're welcome. Added info about the spread.
1

A vanilla solution can be as simple as a filter() call on the first element of the array checking to see that every() subsequent element contains some() elements that match.

const [srcElement, ...compArray] = [...myArray];

const intersection = srcElement.filter(o => (
  compArray.every(arr => arr.some(p => p.id === o.id)))
);

console.log(intersection)
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script>
const myArray = [
  [{ id: 1, name: 'Liam' }, { id: 2, name: 'Oliver' }, { id: 3, name: 'Jake' }],
  [{ id: 1, name: 'Liam' }, { id: 2, name: 'Oliver' }, { id: 4, name: 'Joe' }],
  [{ id: 1, name: 'Liam' }, { id: 2, name: 'Oliver' }, { id: 5, name: 'Dean' }, { id: 6, name: 'Mara' }]
]
</script>

Comments

0

Use nested forEach loops and Set. Go over each sub-array and find out the common items so far.

const intersection = ([firstArr, ...restArr]) => {
  let common = new Set(firstArr.map(({ id }) => id));
  restArr.forEach((arr) => {
    const newCommon = new Set();
    arr.forEach(({ id }) => common.has(id) && newCommon.add(id));
    common = newCommon;
  });
  return firstArr.filter(({ id }) => common.has(id));
};

const myArray = [
  [
    { id: 1, name: "Liam" },
    { id: 2, name: "Oliver" },
    { id: 3, name: "Jake" },
  ],
  [
    { id: 1, name: "Liam" },
    { id: 2, name: "Oliver" },
    { id: 4, name: "Joe" },
  ],
    [
    { id: 2, name: "Oliver" },
    { id: 4, name: "Joe" },
  ],
];

console.log(intersection(myArray));

Comments

0

Nowadays vanilla ES is pretty powerful to work with collections in a functional way even without the help of utility libraries. You can use regular Array's methods to get a pure JS solution. I've created two examples with pure JS. Of course, there could be more approaches as well. And if you already use Lodash in your application, probably it would be better to just use its high-level implementation in form of _.intersectionBy() proposed above to reduce the code complexity.

const myArray = [
  [
    {id: 1, name: 'Liam'},
    {id: 2, name: 'Oliver'},
    {id: 3, name: 'Jake'},
  ],
  [
    {id: 1, name: 'Liam'},
    {id: 2, name: 'Oliver'},
    {id: 4, name: 'Joe'},
  ],
];

// Regular functional filter-reduce
const reducer = (accum, x) => {
  return accum.findIndex(y => x.id == y.id) < 0
    ? [...accum, x]
    : accum;
};

const resultFilterReduce = myArray
  .flat()
  .filter(x => myArray.every(y => y.findIndex(obj => obj.id === x.id) > -1))
  .reduce(reducer, []);

console.log(resultFilterReduce);

// Filter-reduce with using of "HashMap" to remove duplicates
const resultWithHashMap = Object.values(
  myArray
    .flat()
    .filter(x => myArray.every(y => y.findIndex(obj => obj.id === x.id) > -1))
    .reduce((accum, x) => {
      accum[x.id] = x;
      return accum;
    }, {})
  );

console.log(resultWithHashMap);

3 Comments

They actually return flat array of unique (by id) objects as expected in the example in the question.
The OP's expected output is the intersection [{id: 1, name: 'Liam'}, {id: 2, name: 'Oliver'}] (elements that appear at least once in every array). You are returning the union [{ id: 1, name: 'Liam' }, { id: 2, name: 'Oliver' }, { id: 3, name: 'Jake' }, { id: 4, name: 'Joe' }] (all elements that appear in all arrays).
@pilchard I was inattentive yesterday when created a dumb "find unique" answer. Thanks for highlighting the real topic starter's problem for me. I edited my answer to solve the problem in the question with those filter-reduce and "HashMap" approaches I thought about before but in correct way. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.