Aims.kar
Aims.kar

Reputation: 151

Filtering an object based on an parent key in the object (Javascript)

Unfiltered Object

I have this JSON object:

  items = [
    {id: 1, name: "home",  parent: 0, active: 1,  order: 0},
    {id: 2, name: "dashboard", parent: 0, active: 1, order: 1},
    {id: 3, name: "report1",  parent: 2, active: 1, order: 11},
    {id: 4, name: "analytics", parent: 0, active: 1, order: 2},
    {id: 5, name: "report2", parent: 2, active: 1, order: 21},
    {id: 6, name: "report3",  parent: 2, active: 1, order: 22},
    {id: 7, name: "analytics_page1",  parent: 4, active: 1, order: 23}
    
  ]

Object I want

I want to filter it by parent, keeping any with a parent number of 0, and moving any values with a parent number that matches the id into its children. So I want something like this:

  itemsUpdated= [
    { id: 1, 
      name: "home",  
      parent: 0, 
      active: 1,  
      order: 0, 
      children:[]
    },
    { id: 2, 
      name: "dashboard", 
      parent: 0, 
      active: 1, 
      order: 1, 
      children:[
         {id: 3, name: "report1",  parent: 2, active: 1, order: 11, children: []},
         {id: 5, name: "report2", parent: 2, active: 1, order: 21, children:[]},
         {id: 6, name: "report3",  parent: 2, active: 1, order: 22, children:[]}
      ]
    },
    { id: 4, 
      name: "analytics", 
      parent: 0, 
      active: 1, 
      order: 2, 
      children:[
         {id: 7, name: "analytics_page1",  parent: 4, active: 1, order: 23, children:[]}
      ]
    }
  ]

My Approach

so far I have managed to add a children key with an empty array to every item:

 let itemsUpdated = items;
    for(let i = 0 ; i < itemsUpdated .length; i++){
      itemsUpdated [i].children = [];
    }

//MY UPDATED ITEMS LOOKS LIKE THIS 
 updatedItems = [
    {id: 1, name: "home",  parent: 0, active: 1,  order: 0, children:[]},
    {id: 2, name: "dashboard", parent: 0, active: 1, order: 1, children:[]},
    {id: 3, name: "report1",  parent: 2, active: 1, order: 11, children:[]},
    {id: 4, name: "analytics", parent: 0, active: 1, order: 2, children:[]},
    {id: 5, name: "report2", parent: 2, active: 1, order: 21, children:[]},
    {id: 6, name: "report3",  parent: 2, active: 1, order: 22, children:[]},
    {id: 7, name: "analytics_page1",  parent: 4, active: 1, order: 23, children:[]}

  ]

How would I go about filtering and reducing this array down ?

Upvotes: 2

Views: 474

Answers (4)

pilchard
pilchard

Reputation: 12953

This is a case for reduce(), accumulating each object into the children array of its parent indexed by id in the accumulator object. The result is the array stored in the ['0'] property of the returned object.

The advantage of this over some of the other approaches is that it doesn't employ nested loops.

(items array edited from question to include nested children: id: 7 is a child of id: 6)

const items = [
  { id: 3, name: "report1", parent: 2, active: 1, order: 11 },
  { id: 1, name: "home", parent: 0, active: 1, order: 0 },
  { id: 2, name: "dashboard", parent: 0, active: 1, order: 1 },
  { id: 4, name: "analytics", parent: 0, active: 1, order: 2 },
  { id: 5, name: "report2", parent: 2, active: 1, order: 21 },
  { id: 6, name: "report3", parent: 2, active: 1, order: 22 },
  { id: 7, name: "analytics_page1", parent: 6, active: 1, order: 23 }
]

const result = items.reduce((a, o) => {
  a[o.id] = a[o.id] || [];
  a[o.parent] = a[o.parent] || [];

  a[o.parent].push({ ...o, children: a[o.id] });

  return a;
}, {})['0'];

console.log(JSON.stringify(result, null, 2));
.as-console-wrapper { max-height: 100% !important; top: 0; }

A little more concise using logical nullish assignment (??=)

const items = [
  { id: 3, name: "report1", parent: 2, active: 1, order: 11 },
  { id: 1, name: "home", parent: 0, active: 1, order: 0 },
  { id: 2, name: "dashboard", parent: 0, active: 1, order: 1 },
  { id: 4, name: "analytics", parent: 0, active: 1, order: 2 },
  { id: 5, name: "report2", parent: 2, active: 1, order: 21 },
  { id: 6, name: "report3", parent: 2, active: 1, order: 22 },
  { id: 7, name: "analytics_page1", parent: 6, active: 1, order: 23 }
]

const result = items
  .reduce((a, o) => (
    (a[o.parent] ??= []).push({ ...o, children: (a[o.id] ??= []) }), a), {}
  )['0'];

console.log(JSON.stringify(result, null, 2));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

Roman Gherta
Roman Gherta

Reputation: 840

I also want to submit my trial with recursion in mind for nested children, you can see the example of id: 4

let items = [
  { id: 1, name: "home", parent: 0, active: 1, order: 0 },
  { id: 2, name: "dashboard", parent: 0, active: 1, order: 1 },
  { id: 3, name: "report1", parent: 2, active: 1, order: 11 },
  { id: 4, name: "analytics", parent: 3, active: 1, order: 2 },
  { id: 5, name: "report2", parent: 2, active: 1, order: 21 },
  { id: 6, name: "report3", parent: 2, active: 1, order: 22 },
  { id: 7, name: "analytics_page1", parent: 4, active: 1, order: 23 }
];

//adds children empty array
items.forEach((item) => {
  item.children = [];
  return item;
});

//filters parents on 0 level first
let newItems = items.filter((item) => item.parent === 0);

//recursion
items.forEach((item) => searchParent(newItems, item));

console.log(newItems);

function searchParent(someArray, childObject) {
  someArray.forEach((parent) => {
    if (parent.id === childObject.parent) {
      parent.children.push(childObject);
    } else {
      if (parent.children) searchParent(parent.children, childObject);
    }
  });
}

Upvotes: 0

Uly
Uly

Reputation: 11

  const items = [
    { id: 1, name: 'home', parent: 0, active: 1, order: 0 },
    { id: 2, name: 'dashboard', parent: 0, active: 1, order: 1 },
    { id: 3, name: 'report1', parent: 2, active: 1, order: 11 },
    { id: 4, name: 'analytics', parent: 0, active: 1, order: 2 },
    { id: 5, name: 'report2', parent: 2, active: 1, order: 21 },
    { id: 6, name: 'report3', parent: 2, active: 1, order: 22 },
    { id: 7, name: 'analytics_page1', parent: 4, active: 1, order: 23 }
  ];

  const itemsUpdated = items
    .filter(el => !el.parent)
    .map((el, idx) => {
      el.children = [];
      items.forEach(e => {
        if (e.parent === idx+1) {
          el.children.push(e);
        }
      });
      return el;
    });

console.log(itemsUpdated);

Upvotes: 0

The Bomb Squad
The Bomb Squad

Reputation: 4337

ik, ik, im the latest to answer but this was a really fun question :D

let items = [
  {id: 1, name: "home",  parent: 0, active: 1,  order: 0},
  {id: 2, name: "dashboard", parent: 0, active: 1, order: 1},
  {id: 3, name: "report1",  parent: 2, active: 1, order: 11},
  {id: 4, name: "analytics", parent: 0, active: 1, order: 2},
  {id: 5, name: "report2", parent: 2, active: 1, order: 21},
  {id: 6, name: "report3",  parent: 2, active: 1, order: 22},
  {id: 7, name: "analytics_page1",  parent: 4, active: 1, order: 23}
]
//I assume you wont want the original items(since with reference logic.. some editing to this will be done)
let itemsUpdated = items //turn this line into 'let itemsUpdated = JSON.parse(JSON.stringify(items))' if you don't want items edited
//firstly a finder function to return elements which parents match an id n
function findMatches(n){
  let arr=[]
  itemsUpdated.forEach(a=>{if(a.parent==n){arr.push(a)}})
  return arr
}
//now to link >:D
itemsUpdated.forEach(a=>{
  a.children=[] //your for loop's equivalent :D
  let matches=findMatches(a.id)
  if(matches.length){
    matches.forEach(b=>{a.children.push(b)})
  }
})
//now to filter as the finishing touch
itemsUpdated=itemsUpdated.filter(a=>a.parent==0)
console.log(itemsUpdated)

Upvotes: 0

Related Questions