简体   繁体   中英

Flatten a deeply nested array with objects and arrays

I have an array of objects that contain another array with objects. The nesting is four levels deep. The structure of the array is:

[
  {
    title: 'Title',
    type: 'section',
    links: [
      {
        label: 'Label',
        id: 'id_1',
        links: [
          {
            title: 'Title',
            type: 'section',
            links: [
              {
                label: 'Label',
                id: 'id_2',
                links: [
                  {
                    label: 'Label',
                    id: 'id_3',
                    links: [],
                  }
                ]
              }
            ]
          },
          {
            title: 'Other title',
            type: 'section',
            links: [
              {
                label: 'Label',
                id: 'id_4',
                links: [],
              }
            ]
          }
        ]
      }
    ]
  }
]

I want to have a flattened array with the id's of the link arrays that contain links (they are parents of submenu's). So the desired outcome is like: ["id_1", "id_2"]

I have tried to get the outcome with this function taken from MDN :

flatDeep(arr, d = 1) {
  return d > 0
    ? arr.reduce((acc, val) =>
      acc.concat(Array.isArray(val.links)
        ? this.flatDeep(val.links, d - 1)
        : val.links), [])
    : arr.slice();
}

This gives me an empty array.

I think recursive function will simplify. (recursively look for lists array and push the id into res).

 const data = [ { title: "Title", type: "section", links: [ { label: "Label", id: "id_1", links: [ { title: "Title", type: "section", links: [ { label: "Label", id: "id_2", links: [ { label: "Label", id: "id_3", links: [] } ] } ] }, { title: "Other title", type: "section", links: [ { label: "Label", id: "id_4", links: [] } ] } ] } ] } ]; const res = []; const ids = data => { data.forEach(item => { if ("id" in item) { res.push(item.id); } if (item.links) { ids(item.links); } }); }; ids(data); console.log(res);

You could get a flat array with a recursion and a check for id for missing property.

 const getId = ({ id, links }) => [ ...(id === undefined ? [] : [id]), ...links.flatMap(getId) ], data = [{ title: 'Title', type: 'section', links: [{ label: 'Label', id: 'id_1', links: [{ title: 'Title', type: 'section', links: [{ label: 'Label', id: 'id_2', links: [{ label: 'Label', id: 'id_3', links: [] }] }] }, { title: 'Other title', type: 'section', links: [{ label: 'Label', id: 'id_4', links: [] }] }] }] }], result = data.flatMap(getId); console.log(result);

 var array = JSON.parse('[{"title":"Title","type":"section","links":[{"label":"Label","id":"id_1","links":[{"title":"Title","type":"section","links":[{"label":"Label","id":"id_2","links":[{"label":"Label","id":"id_3","links":[]}]}]},{"title":"Other title","type":"section","links":[{"label":"Label","id":"id_4","links":[]}]}]}]}]'); arr = []; while(array.length != 0) { var ob1 = array.splice(0,1)[0]; for(var ob2 of ob1.links) { if (ob2.links.length !== 0) { arr.push(ob2.id); array = array.concat(ob2.links); } } } console.log(arr);

Here's the output as you requested:

[
  "id_1",
  "id_2"
]

Use Array.flatMap() . Destructure each object and use an empty array as default for missing id values. Concat the id and the result of flattening the links recursively.

 const flattenIds = arr => arr.flatMap(({ id = [], links }) => [].concat(id, flattenIds(links)) ); const data = [{ title: 'Title', type: 'section', links: [{ label: 'Label', id: 'id_1', links: [{ title: 'Title', type: 'section', links: [{ label: 'Label', id: 'id_2', links: [{ label: 'Label', id: 'id_3', links: [] }] }] }, { title: 'Other title', type: 'section', links: [{ label: 'Label', id: 'id_4', links: [] }] }] }] }]; const result = flattenIds(data); console.log(result);

Here is a non-recursive version.

 const data = [{title:'Title',type:'section',links:[{label:'Label',id:'id_1',links:[{title:'Title',type:'section',links:[{label:'Label',id:'id_2',links:[{label:'Label',id:'id_3',links:[]}]}]},{title:'Other title',type:'section',links:[{label:'Label',id:'id_4',links:[]}]}]}]}]; const stack = data.slice(); const result = []; let obj; while (obj = stack.shift()) { if ("id" in obj && obj.links.length > 0) result.push(obj.id); stack.push(...obj.links); } console.log(result);

This uses breath first, but can easily be changed into depth first. You'll only have to change the stack.push call into stack.unshift .

For a more detailed explanation about the two, check out Breadth First Vs Depth First .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM