Dan Reil
Dan Reil

Reputation: 387

Flattern object in nested array of arrays Javascript

I have an array of arrays, which contain objects, would like to get the value of a certain key and return it as a big array, have tried a nested map but it returns multiple array's rather than a single array.

const items = [
  {
    id: 1,
    sub_items: [
      {
        id: 1
      },
      {
        id: 2
      },
      {
        id: 3
      }
    ]
  },
  {
    id: 2,
    sub_items: [
      {
        id: 4
      },
      {
        id: 5
      },
      {
        id: 6
      }
    ]
  }
]

const subItemIDs = items.map( (item) =>
  item.sub_items.map( (subItem) => subItem.id )
)

console.log(subItemIDs);

Expected output

[1, 2, 3, 4, 5, 6]

Actual output

[ [1,2,3], [4,5,6] ]

Upvotes: 1

Views: 104

Answers (5)

Mike
Mike

Reputation: 1327

As is the case with most things JavaScript, you have several options. Some are more efficient than others, others have a certain stylistic purity, others might better speak to your fancy. Here are a few:

Array.flat

With array flat you can take your original code and have the JS Engine flatten the array down to a one-dimensional array. Simply append .flat() onto the end of your map.

const items = [
  { id: 1, sub_items: [ { id: 1 }, { id: 2 }, { id: 3 }, ] },
  { id: 2, sub_items: [ { id: 4 }, { id: 5 }, { id: 6 }, ] },
];

const subItemIds = items.map( (item) =>
  item.sub_items.map( (subItem) => subItem.id )
).flat()

console.log(subItemIds);

Array.reduce

Another method is to use reduce to iterate over the object and build an accumulation array using Array.reduce. In the example below, when pushing onto the array, the spread operator (...) is used to break the array into elements.

const items = [
  { id: 1, sub_items: [ { id: 1 }, { id: 2 }, { id: 3 }, ] },
  { id: 2, sub_items: [ { id: 4 }, { id: 5 }, { id: 6 }, ] },
];

const subItemIds = items.reduce((arr,item) => (
   arr.push(...item.sub_items.map((subItem) => subItem.id)), arr
),[])

console.log(subItemIds);

Other

Other answers here make use of custom functions or Array.flatMap, which should be explored as they could lead to more readable and efficient code, depending on the program's needs.

Upvotes: 0

Nicholas Carey
Nicholas Carey

Reputation: 74267

Sometimes, the obvious is the easiest: Given a data structure that looks like this

const items = [
  { id: 1, sub_items: [ { id: 1 }, { id: 2 }, { id: 3 }, ] },
  { id: 2, sub_items: [ { id: 4 }, { id: 5 }, { id: 6 }, ] },
];

A trivial function like this

function extract_item_ids( items ) {
  const ids = [];
  for ( const item of items ) {
    for ( const {id} of sub_items ) {
      ids.push(id);
    }
  }
  return ids;
}

should do the trick. If you want to collect the ids from a tree of any depth, it's just as easy:

function extract_item_ids( items ) {
  const ids = [];

  const pending = items;
  while ( pending.length > 0 ) {
    const item = pending.pop();

    ids.push(item.id);
    pending.push(...( item.sub_items || [] ) );

  }

  return ids;
}

And collecting the set of discrete item IDs is no more difficult: If you want to collect the ids from a tree of any depth, it's just as easy:

function extract_item_ids( items ) {
  const ids = new Set();

  const pending = [...items];
  while ( pending.length > 0 ) {
    const item = pending.pop();

    ids.add(item.id);
    pending.push(...( item.sub_items || [] ) );

  }

  return Array.from(ids);
}

Upvotes: 0

Dan Reil
Dan Reil

Reputation: 387

Achieved this with:

const items = [
  {
    id: 1,
    sub_items: [
      {
        id: 1
      },
      {
        id: 2
      },
      {
        id: 3
      }
    ]
  },
  {
    id: 2,
    sub_items: [
      {
        id: 4
      },
      {
        id: 5
      },
      {
        id: 6
      }
    ]
  }
]

const subItemIDs = [].concat(...items.map( (item) =>
  item.sub_items.map( (subItem) => subItem.id )
))

console.log(subItemIDs);

Upvotes: 1

Rajdeep D
Rajdeep D

Reputation: 3910

You can use arrays.flat(). I can provide more specific code once output is mentioned in the question

const arr1 = [0, 1, 2, [3, 4]];

console.log(arr1.flat());
// expected output: [0, 1, 2, 3, 4]

const arr2 = [0, 1, 2, [[[3, 4]]]];

console.log(arr2.flat(2));
// expected output: [0, 1, 2, [3, 4]]

Upvotes: 2

Nina Scholz
Nina Scholz

Reputation: 386578

You could take Array#flatMap to get a flat array from nested arrays.

const
    items = [{ id: 1, sub_items: [{ id: 1 }, { id: 2 }, { id: 3 }] }, { id: 2, sub_items: [{ id: 4 }, { id: 5 }, { id: 6 }] }],
    subItemIDs = items.flatMap(({ sub_items }) => sub_items.map(({ id }) => id));

console.log(subItemIDs);

Upvotes: 1

Related Questions