Search code examples
javascriptangularjstypescriptlodash

Compare 2 arrays of objects by 2 different keys. And make one nested object which contains parent and child


I have problem with comparing 2 arrays of objects. I was searching lodash documentation, but I was not able to find proper method. The thing is that I need to compare objects by different keys.

private parentArray: {}[] = [
    { Id: 1, Name: 'A' },
    { Id: 2, Name: 'B' },
    { Id: 3, Name: 'C' },
    { Id: 4, Name: 'D' }
  ];

private childArray: {}[] = [
    { Id: 2, parentId: 2, Name: 'a' },
    { Id: 3, parentId: 2, Name: 'b' },
    { Id: 4, parentId: 4, Name: 'c' },
    { Id: 5, parentId: 4, Name: 'd' }
  ];

I need to make a new array of nested objects where 'parentId' will be matched with 'Id' of parent which will look like that:

private newArray = [
    { Id: 1, Name: 'A', Children: [] },
    {
      Id: 2,
      Name: 'B',
      Children: [
        { Id: 2, parentId: 2, Name: 'a' },
        { Id: 3, parentId: 2, Name: 'b' }
      ]
    },
    {
      Id: 3,
      Name: 'C',
      Children: []
    },
    {
      Id: 4,
      Name: 'D',
      Children: [
        { Id: 4, parentId: 4, Name: 'c' },
        { Id: 5, parentId: 4, Name: 'd' }
      ]
    }
  ];

I was using '.intersectionWith([arrays], [comparator])' and '.isMatchWith(object, source, [customizer])' but it did not give me what I need. I will be grateful for any help.


Solution

  • The simplest way to do this is probably

    const newArray = parentArray.map(
        p => ({ ...p, Children: childArray.filter(c => c.parentId === p.Id) })
    )
    

    which results in the output you are looking for. Note that this is not necessarily the best performing algorithm in the case where parentArray and childArray are large, since we are looping over the entire childArray for every element in parentArray (so if childArray is length 𝑐 and parentArray is length 𝑝 then this algorithm is O(𝑝×𝑐)).

    If such performance matters then you can do it by iterating over each element in childArray and parentArray once (which is O(𝑝+𝑐) assuming hash lookups are O(1)), like this:

    type Parent = typeof parentArray[number];
    type Child = typeof childArray[number];
    interface New extends Parent {
        Children: Child[];
    }
    
    const newArray: New[] = [];
    const parentLookup: Record<number, New> = {};    
    for (const p of parentArray) {
        const n = { ...p, Children: [] };
        newArray.push(n)
        parentLookup[p.Id] = n;
    }
    for (const c of childArray) {
        parentLookup[c.parentId]?.Children.push(c);
    }
    console.log(newArray);
    

    Playground link to code