Recursive Map function

Viewed 14935

I'm having hard time figuring out how to do this recursive map function. I have an array that look like this.

var array = [
      {
        id: 1,
        label: 'Satisfied customers',
        children: [
          {
            id: 2,
            label: 'Good food',
            icon: 'restaurant_menu',
            children: [

              { id: 3, label: 'Quality ingredients'},
              { id: 4, label: 'Good recipe' }
            ]
          },
          {
            id: 5,
            label: 'Good service',
            icon: 'room_service',
            children: [
              { id: 6, label: 'Prompt attention' },
              { id: 7, label: 'Professional waiter' }
            ]
          },
          {
            id: 8,
            label: 'Pleasant surroundings',
            icon: 'photo',
            children: [
              {
                id: 9,
                label: 'Happy atmosphere (not tickable)',
                tickable: false,
              },
              {
                id: 10,
                label: 'Good table presentation (disabled node)',
                disabled: true,
              },
              {
                id: 11,
                label: 'Pleasing decor',
              }
            ]
          },
          {
            id: 12,
            label: 'Extra information (has no tick)',
            noTick: true,
            icon: 'photo'
          },
          {
            id: 13,
            label: 'Forced tick strategy (to "strict" in this case)',
            tickStrategy: 'strict',
            icon: 'school',
            children: [
              {
                id: 14,
                label: 'Happy atmosphere',
              },
              {
                id: 15,
                label: 'Good table presentation',
              },
              {
                id: 16,
                label: 'Very pleasing decor',
              }
            ]
          }
        ]
      }
    ];

This is the array looks like... enter image description here

As you can see the children is recursive.

I need to put them into one array. My code doesn't work an has an error.

const result = [];   
const map = (e) => {

    result.push({
        id: e.id,
        label: e.label,
    })

    e.children.map(map)


};

array.map(map);

the error is on e.children.map(map). enter image description here

I need to push them all in array variable but I don't know how to do this. TY

2 Answers

You need to check if current item has children element, and you can use forEach instead because map return new array and forEach just go throw each element.

const cb = (e) => {
    res.push({
        id: e.id,
        label: e.label,
    });
    e.children && e.children.forEach(cb);
}
array.forEach(cb);

A great way to learn about mutual recursion using vanilla JavaScript -

const transform1 = ({ id = 0, label = "", children = [] }) =>
  [ { id, label }, ...transformAll (children) ] // calls transformAll

const transformAll = (children = []) =>
  children .flatMap (c => transform1 (c)) // calls transform1

console.log(transformAll(array))

Output -

[
  {
    "id": 1,
    "label": "Satisfied customers"
  },
  {
    "id": 2,
    "label": "Good food"
  },
  {
    "id": 3,
    "label": "Quality ingredients"
  },
  {
    "id": 4,
    "label": "Good recipe"
  },
  {
    "id": 5,
    "label": "Good service"
  },
  {
    "id": 6,
    "label": "Prompt attention"
  },
  {
    "id": 7,
    "label": "Professional waiter"
  },
  {
    "id": 8,
    "label": "Pleasant surroundings"
  },
  {
    "id": 9,
    "label": "Happy atmosphere (not tickable)"
  },
  {
    "id": 10,
    "label": "Good table presentation (disabled node)"
  },
  {
    "id": 11,
    "label": "Pleasing decor"
  },
  {
    "id": 12,
    "label": "Extra information (has no tick)"
  },
  {
    "id": 13,
    "label": "Forced tick strategy (to \"strict\" in this case)"
  },
  {
    "id": 14,
    "label": "Happy atmosphere"
  },
  {
    "id": 15,
    "label": "Good table presentation"
  },
  {
    "id": 16,
    "label": "Very pleasing decor"
  }
]

Expand the snippet below the verify the results in your own browser -

var array = [
      {
        id: 1,
        label: 'Satisfied customers',
        children: [
          {
            id: 2,
            label: 'Good food',
            icon: 'restaurant_menu',
            children: [

              { id: 3, label: 'Quality ingredients'},
              { id: 4, label: 'Good recipe' }
            ]
          },
          {
            id: 5,
            label: 'Good service',
            icon: 'room_service',
            children: [
              { id: 6, label: 'Prompt attention' },
              { id: 7, label: 'Professional waiter' }
            ]
          },
          {
            id: 8,
            label: 'Pleasant surroundings',
            icon: 'photo',
            children: [
              {
                id: 9,
                label: 'Happy atmosphere (not tickable)',
                tickable: false,
              },
              {
                id: 10,
                label: 'Good table presentation (disabled node)',
                disabled: true,
              },
              {
                id: 11,
                label: 'Pleasing decor',
              }
            ]
          },
          {
            id: 12,
            label: 'Extra information (has no tick)',
            noTick: true,
            icon: 'photo'
          },
          {
            id: 13,
            label: 'Forced tick strategy (to "strict" in this case)',
            tickStrategy: 'strict',
            icon: 'school',
            children: [
              {
                id: 14,
                label: 'Happy atmosphere',
              },
              {
                id: 15,
                label: 'Good table presentation',
              },
              {
                id: 16,
                label: 'Very pleasing decor',
              }
            ]
          }
        ]
      }
    ];
    
const transform1 = ({ id = 0, label = "", children = [] }) =>
  [ { id, label }, ... transformAll (children) ]
  
const transformAll = (children = []) =>
  children .flatMap (c => transform1 (c))
  
console.log(transformAll(array))


A tour of Flat Earth

If you've never seen .flatMap before -

xs.flatMap(f) == xs.map(f).reduce((a,b) => a.concat(b), [])
xs.flatMap(f) == xs.reduce((a,b) => a.concat(f(b)), [])

It's best seen with a simple demo -

const twice = x =>
  [ x, x ]
  
console .log
  ( [ 'a', 'b', 'c' ] .flatMap (twice) // [ 'a', 'a', 'b', 'b', 'c', 'c' ]
  , [ 1, 2, 3, 4, 5 ] .flatMap (twice) // [ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 ]
  )

flatMap is useful for all kinds of things -

const tree =
  [ 0, [ 1 ], [ 2, [ 3 ], [ 4, [ 5 ] ] ] ]
  
const all = ([ value, ...children ]) =>
  [ value, ...children .flatMap (all) ]
  
console .log (all (tree))
// [ 0, 1, 2, 3, 4, 5 ]

really cool things -

const ranks =
  [ 'J', 'Q', 'K', 'A' ]
  
const suits =
  [ '♡', '♢', '♤', '♧' ]

const cards =
  ranks .flatMap (r =>
  suits .flatMap (s =>
  [ [ r, s ] ]
  ))

console .log (cards)
// [ ['J','♡'], ['J','♢'], ['J','♤'], ['J','♧']
// , ['Q','♡'], ['Q','♢'], ['Q','♤'], ['Q','♧']
// , ['K','♡'], ['K','♢'], ['K','♤'], ['K','♧']
// , ['A','♡'], ['A','♢'], ['A','♤'], ['A','♧']
// ]

Related