Rijo
Rijo

Reputation: 3043

Filter array of objects based on another array

I have a source array and target array, based on the target array need to update the source array

sourceAry = [{name:'Label1', value: 'label1', children:[{name:'Ammu'},{name:'Rahual'},{name:'Anu'}]},
{name:'Label2', value: 'label2', children:[{name:'Hari'},{name:'Tom'}]},
];

targetAry = [{name:'Label1', value: 'label1', children:[{name:'Anu'}]},
{name:'Label2', value: 'label2', children:[{name:'Hari'},{name:'Tom'}]},
];

resultAry = [{name:'Label1', value: 'label1', children:[{name:'Ammu'},{name:'Rahual'}]}
]},
];

Code which I try

let resultAry = sourceAry.map((obj) => {
      obj.children.map((elem) =>{
        targetAry.filter(parent => parent.children.filter((el) => {
          el.name !== elem.name}))
      })
    })
    console.log(resultAry, 'NEW', list);

Upvotes: 0

Views: 80

Answers (2)

Giovanni Esposito
Giovanni Esposito

Reputation: 11156

Ciao, try something like this:

sourceAry = [{name:'Label1', value: 'label1', children:[{name:'Ammu'},{name:'Rahual'},{name:'Anu'}]}, {name:'Label2', value: 'label2', children:[{name:'Hari'},{name:'Tom'},{name:'Ammu'},{name:'Rahual'},{name:'Anu'}]}, {name:'Label3', value: 'label3', children:[{name:'Ammu'},{name:'Rahual'},{name:'Anu'}]} ];

targetAry = [{name:'Label1', value: 'label1', children:[{name:'Anu'}]},
{name:'Label2', value: 'label2', children:[{name:'Hari'},{name:'Tom'}]},
];

let result = [];
sourceAry.forEach(source => {
       let filter = targetAry.filter(target => target.name === source.name)
       if (filter.length > 0) {      
           let filterchildren = source.children.filter(a => !filter[0].children.map(b=>b.name).includes(a.name));
           if (filterchildren.length > 0) {
              let resultobj = source;
              resultobj.children = filterchildren;
              result.push(resultobj);
           }
       }
       else result.push(source);
})

console.log(result)

I filter targetAry based on sourceAry name. Then subtract children with .filter(a => !filter[0].children.map(b=>b.name).includes(a.name)); and finally push element found in result array.

Upvotes: 1

leetwinski
leetwinski

Reputation: 17859

you could start start with some facilities to make it simpler:

const indexBy = (f, data) => data.reduce((acc, x) => Object.assign(acc, { [f(x)]: x }), {})

const remove = (keyFn, dataToRemove, from) => {
  const dataToRemoveIndexed = indexBy(keyFn, dataToRemove);
  return from.filter(it => !(keyFn(it) in dataToRemoveIndexed));
}

we introduce the indexBy here, to make removal O(m+n), instead of O(m^2) (if there are many items in the collection to check)

then you can use it like this:

const targetIndexed = indexBy(it => it.name, targetAry);

const result = sourceAry.map(
  it => ({ 
    ...it, 
    children: remove(
      it => it.name, 
      (targetIndexed[it.name] || {}).children || [],
      it.children
    )
  })
)

so it leaves you with the following result:

[
 {"name":"Label1","value":"label1","children":[{"name":"Ammu"}, {"name":"Rahual"}]},
 {"name":"Label2","value":"label2","children":[]}
]

if you also want to delete the item with empty children, you can just filter it out: result.filter(it => it.children.length > 0)

Upvotes: 1

Related Questions