Miguel Stevens
Miguel Stevens

Reputation: 9201

Flatten an array with nested objects

I have an array with objects, that can have children, the children have the same structure as the parent, it's just object nesting basically.

I'm wondering how I can flatten the structure of my objects so I have the id's of all the objects, including the nested one's.

For example, This structure

const data = [
  {
    id: 2,
    children: [
      {
        id: 1,
        children: []
      }
    ]
  },
  {
    id: 3,
    children: [],
  }
]

Should be flattened to this

const data = [2,1,3]

I've tried

Using Array.reduce() and the object spread syntax, but I can't wrap my head around the logic required to do this.

Upvotes: 2

Views: 1783

Answers (9)

Nina Scholz
Nina Scholz

Reputation: 386578

You could reduce the array of objects by using the actual id and get their children objects.

const
    getId = array => array.reduce(
        (r, { id, children }) => [...r, id, ...getId(children)],
        []
    ),
    data = [{ id: 2, children: [{ id: 1, children: [] }] }, { id: 3, children: [] }],
    ids = getId(data);
    
console.log(ids);

Upvotes: 1

Someone
Someone

Reputation: 3568

Another version. Not the prettiest but gets the job done:

const data = [
    {
        id: 2,
        children: [
            {
                id: 1,
                children: []
            }
        ]
    },
    {
        id: 3,
        children: [],
    }
];
    
let mappedArray = data.map(num => [].concat(num.children.map(child => child.id)).concat(num.id));
    mappedArray = [].concat.apply([], mappedArray);

console.log(mappedArray);

Upvotes: 0

mplungjan
mplungjan

Reputation: 177940

I do not like recursions :)

Do note the other Stringify answer - ILST
https://stackoverflow.com/a/55179326/295783

const data=[{id:2,children:[{id:1,children:[]}]},{id:3,children:[],}];

console.log(
  JSON.stringify(data)
    .match(/(?:"id":)(\d+)/g)
    .map(v => +v.replace(/"id":/g, ""))
)

I however wish someone could find me a way to ignore the non-capturing group in one go

Upvotes: 1

Maheer Ali
Maheer Ali

Reputation: 36574

You can use recursion.Note that is below code reference of arr is passed so we can directly push() ids to it and no need to get return value

const data = [{ id: 2, children: [{ id: 1, children: [] }] }, { id: 3, children: [], } ]

function getIds(data,arr){
  //iterate over array of chilren
  for(let child of data){
    //add id of each child to arr
    arr.push(child.id);
    //check if child have children add its 'ids' to same array
    if(child.children) getIds(child.children,arr);
  }
  //return array in end
  return arr;
}
console.log(getIds(data,[]))

Upvotes: 1

wang
wang

Reputation: 1780

const data = [
  {
    id: 2,
    children: [
      {
        id: 1,
        children: []
      }
    ]
  },
  {
    id: 3,
    children: [],
  }
]

const getIds = (data) => data.map(d => [d.id, ...getIds(d.children)]).flat()

console.log(getIds(data))

Upvotes: 5

Hassan Imam
Hassan Imam

Reputation: 22534

You can use a recursive approach and iterate for each children and push all the id in an array.

const data = [{ id: 2, children: [{ id: 1, children: [] }] }, { id: 3, children: [], } ],
      getId = (data) => data.reduce((r,{id, children}) => r.concat(id, getId(children)),[]);
console.log(getId(data));

Upvotes: 0

Pranav C Balan
Pranav C Balan

Reputation: 115222

You can do some recursive approach.

function flatArr(arr, res) {
  // iterate over the array
  arr.forEach(o => {
    // check id is present then push it into the result array
    if ('id' in o) res.push(o.id)
    // check children is present and non-empty
    // then ecursively call the function
    if (o.children && o.children.length) flatArr(o.children, res);
  })
  // return the result array(optional)
  return res;
}

console.log(flatArr(data, []));

const data = [{
    id: 2,
    children: [{
      id: 1,
      children: []
    }]
  },
  {
    id: 3,
    children: [],
  }
];

function flatArr(arr, res) {
  // iterate over the array
  arr.forEach(o => {
    // check id is present then push it into the result array
    if ('id' in o) res.push(o.id)
    // check children is present and non-empty
    // then ecursively call the function
    if (o.children && o.children.length) flatArr(o.children, res);
  })
  // return the result array(optional since it's the same array reference you are passing initially)
  return res;
}

console.log(flatArr(data, []));

Upvotes: 1

Quentin
Quentin

Reputation: 943569

This is a job for recursion. Loop over the array and for each element in it, push the id into a new array and repeat for the children.

const data = [{
    id: 2,
    children: [{
      id: 1,
      children: []
    }]
  },
  {
    id: 3,
    children: [],
  }
];

console.log(flatten(data));

function flatten(data) {
  const result = [];
  recursive(data);
  return result;

  function recursive(data) {
    data.forEach(member => {
      result.push(member.id);
      recursive(member.children);
    });
  }
}

Upvotes: 3

Snow
Snow

Reputation: 4097

You can use JSON.stringify, and for each key of id, push to an array:

const data = [
  {
    id: 2,
    children: [
      {
        id: 1,
        children: []
      }
    ]
  },
  {
    id: 3,
    children: [],
  }
]
const ids = [];
JSON.stringify(data, (key, val) => {
  if (key === 'id') {
    ids.push(val);
  }
  return val;
});
console.log(ids);

Upvotes: 1

Related Questions