-1

Let's say I have the following array containing arrays of objects (songs):

[
  [
    {
      name: 'I Want You Back',
      id: 1
    },
    {
      name: 'ABC',
      id: 2
    }
  ],
  [
    {
      name: 'I Want You Back',
      id: 1
    },
    {
      name: 'Dont Stop Me Now',
      id: 3
    }
  ],
  [
    {
      name: 'I Want You Back',
      id: 1
    },
    {
      name: 'ABC',
      id: 2
    }
  ],
]

I wish to go through my data and return the objects that have been found in every array, so in this case, the returning value would be:

{
   name: 'I Want You Back',
   id: 1
}

So far, I've only managed to do it for arrays of strings, not objects, like so:

const arrays = [
  ['I Want You Back', 'ABC'],
  ['I Want You Back', 'Dont stop me now'],
  ['I Want You Back', 'ABC']
];

const result = arrays.shift().filter((v) => {
  return arrays.every((a) => {
    return a.indexOf(v) !== -1;
  });
});

// result = ["I Want You Back"]

I've been trying, but I can't manage to apply the same logic with my objects (and their name or id for example). I'd really appreciate if some of you show me how you would do it.

Christopher
  • 1,712
  • 2
  • 21
  • 50
  • Possible duplicate of [Difference and intersection of two arrays containing objects](https://stackoverflow.com/questions/33356504/difference-and-intersection-of-two-arrays-containing-objects) and [Typescript : Find common objects from two array using Lambda function](https://stackoverflow.com/questions/50135112) – adiga Mar 03 '19 at 18:08
  • The question concerns more than 2 arrays, which requires a different approach altogether – Christopher Mar 03 '19 at 18:17

1 Answers1

1

You could just count the objects by the unique id then go through and find the objects whose count is the same as the length of your array:

const songs = [[{name: 'I Want You Back',id: 1},{name: 'ABC',id: 2}],[{name: 'I Want You Back',id: 1},{name: 'Dont Stop Me Now',id: 3}],[{name: 'I Want You Back',id: 1},{name: 'ABC',id: 2}],]

// count of each unique id
let counts  = songs.reduce((count, arr) => {
  arr.forEach(obj => {
    if (!count[obj.id]) count[obj.id] = {obj, count: 0}
    count[obj.id].count += 1  
  })
  return count
}, {})

// now filter out all whose count isn't the length
// and map to retreive just the object
let intersect = Object.values(counts)
                .filter(o => o.count == songs.length)
                .map(o => o.obj)

console.log(intersect)

Of course this would be much easier if object equality was tested by value, but since it's not you need to jump through a few extra hoops.

Mark
  • 90,562
  • 7
  • 108
  • 148
  • This seems like a great solution ! I'll test it out and come back to you in a sec. "if object equality was tested by value", meaning each array has instances of the same object? – Christopher Mar 03 '19 at 18:06
  • 1
    Just make sure that each array doesn't contain duplicates as otherwise if you had one array with a duplicate this wouldn't be handled correctly with a counting sort like above. However that would be unusual situation but just though I'd let you know. – Michael Knight Mar 03 '19 at 18:09
  • Noted @MichaelKnight, each song is unique for each array so I shouldn't run into that problem. Thank you – Christopher Mar 03 '19 at 18:10