3

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.

1
  • So, from given array of objects you simply want an array like ["id_1", "id_2"]? Commented Mar 31, 2020 at 17:06

5 Answers 5

3

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);

Sign up to request clarification or add additional context in comments.

Comments

1

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);

1 Comment

Thank Nina! I added a check for empty links array where the id is checked for undefined to return only the id's that have links.
1

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.

Comments

0

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"
]

Comments

0

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);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.