Filter nested array of objects to array of objects

Viewed 77

I have an array of objects which is nested. How do I make an array of objects with this one getting values from nested properties as well? If onClick property is empty, it means there is a property called children and the parent element should not be a part of the new list. I need to loop through the children array and get the values. Please see the expected output below.

const headers = [{
    title: 'Arun',
    id: 'arunId',
    onClick: 'onClickArun'
  },
  {
    title: "George",
    id: 'georgeId',
    onClick: '',
    children: [{
        title: 'David',
        id: 'davidId',
        onClick: 'onClickDavid'
      },
      {
        title: 'Patrick',
        id: 'patrickId',
        onClick: 'onClickPatrick'
      }
    ]
  },
  {
    title: 'Mark',
    id: 'markId',
    onClick: 'onClickMark'
  }
];

console.log(headers.map(item => {
  return {
    title: item.title,
    onClick: item.onClick
  }
}))

Expected Output:

[{
    title: 'Arun',
    onClick: 'onClickArun'
  },
  {
    title: 'David',
    onClick: 'onClickDavid'
  },
  {
    title: 'Patrick',
    onClick: 'onClickPatrick'
  },
  {
    title: 'Mark',
    onClick: 'onClickMark'
  }
]

Any help is greatly appreciated.

4 Answers

You could take Array#flatMap with a recursive callback.

const
    map = ({ title, onClick, children }) => onClick
        ? { title, onClick }
        : children.map(map);

var headers = [{ title: 'Arun', id: 'arunId', onClick: 'onClickArun' }, { title: "George", id: 'georgeId', onClick: '', children: [{ title: 'David', id: 'davidId', onClick: 'onClickDavid' }, { title: 'Patrick', id: 'patrickId', onClick: 'onClickPatrick' }] }, { title: 'Mark', id: 'markId', onClick: 'onClickMark' }],        
    result = headers.flatMap(map);

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

You can do this using Array.prototype.reduce, where you just need to check if the parent onClick prop is empty and the children prop is present or not:

var headers = [{ title: 'Arun', id: 'arunId', onClick: 'onClickArun' }, { title: "George", id: 'georgeId', onClick: '', children: [{ title: 'David', id: 'davidId', onClick: 'onClickDavid' }, { title: 'Patrick', id: 'patrickId', onClick: 'onClickPatrick' }] }, { title: 'Mark', id: 'markId', onClick: 'onClickMark' }];


function getObject(headers, acc){
  return headers.reduce((acc, ele) => {
   if(!ele.onClick.length && ele.children){
      acc = getObject(ele.children, acc);
   }else{
      acc.push({"title": ele.title, "onClick": ele.onClick});
   }
   return acc;
  }, acc);
}
console.log(getObject(headers, []));

Looks like you need a depth first search. You run on the array, for each item with children recurse on it and pass along the existing array. otherwise, add the item to the list.

function getChildren(array, total = []){
  for(let item of array) {
    if(item.children) {
      getChildren(item.children, total)
    } else {
      total.push({
        title: item.title,
        onClick: item.onClick
      })
    }
  }
  return total
}
let headers = [{
  title: 'Arun',
  id: 'arunId',
  onClick: 'onClickArun'
},
{
  title: "George",
  id: 'georgeId',
  onClick: '',
  children: [{
    title: 'David',
    id: 'davidId',
    onClick: 'onClickDavid'
  },
  {
    title: 'Patrick',
    id: 'patrickId',
    onClick: 'onClickPatrick'
  }
  ]
},
{
  title: 'Mark',
  id: 'markId',
  onClick: 'onClickMark'
}
]
// take children, only if onClick is empty
.map(item => item.onClick ? item : item.children)
// make flat array
headers = [].concat.apply([], headers)
    .map(item => { 
        const temp = {};
        temp.title = item.title;
        temp.onClick = item.onClick;
        return temp; // take only onClick and title property from each item
})


Related