0

I have an array of objects. Every object has hasPermission property and children property. children property is also an array of objects and every object has hasPermission property. My array like this:

const navigationMenus = [
  {
    hasPermission: false,
    name: 'Main',
    type: 'section',
    children: [
      {
        hasPermission: true,
        name: 'Test',
        type: 'item',
        link: '/test'
      }
    ]
  },
  {
    hasPermission: true,
    name: 'Master',
    type: 'section',
    children: [
      {
        hasPermission: true,
        name: 'Operator Group',
        type: 'item',
        link: '/operator-group'
      },
      {
        hasPermission: false,
        name: 'Operation Group',
        type: 'item',
        link: '/operation-group'
      }
    ]
  }
];

Based on hasPermission property I want another array which holds only those objects which hasPermission property is true. I tried with this approach.

const permittedNavigationMenus = []
for (let i = 0; i < navigationMenus.length; i++) {
  const section = navigationMenus[i];
  if (section.hasPermission) {
    const permittedSection = {
      name: section.name,
      type: section.type,
      children: []
    }
    for (let j = 0; j < section.children.length; j++) {
      const item = section.children[j]
      if (item.hasPermission) {
        permittedSection.children.push(item)
      }
    }
    permittedNavigationMenus.push(permittedSection)
  }
}
console.log(JSON.stringify(permittedNavigationMenus, null, 2))

Is there any better solution?

Nasir
  • 233
  • 2
  • 12

4 Answers4

1

a simple recursive function can do that:

const navigationMenus = 
  [ { hasPermission: false, name: 'Main', type: 'section', children: 
      [ { hasPermission: true, name: 'Test', type: 'item', link: '/test' } ] 
    } 
  , { hasPermission: true, name: 'Master', type: 'section', children: 
      [ { hasPermission: true, name: 'Operator Group', type: 'item', link: '/operator-group' } 
      , { hasPermission: false, name: 'Operation Group', type: 'item', link: '/operation-group' } 
  ] } ] 

const navigationMenusTrue = []

function runArray(arr,parent)
  {
  arr.forEach(({children,...info}) => 
    {
    if (info.hasPermission)
      {
      let newRow = {...info}
      parent.push(newRow)
      if (children)
        {
        let xChilds = []
        newRow.children = xChilds
        runArray(children,xChilds)
        }
      }  
    })
  }

runArray(navigationMenus,navigationMenusTrue)

console.log( navigationMenusTrue )
.as-console-wrapper {max-height: 100%!important;top:0 }
Mister Jojo
  • 20,093
  • 6
  • 21
  • 40
1

Here's an recursive option using Array#reduce()

const
  filterByPermission = (arr) => {
    return arr.reduce((a, o) => {
      if (o.hasPermission) {
        const _o = o?.children?.length
          ? { ...o, children: filterByPermission(o.children) }
          : { ...o };
        a.push(_o);
      }

      return a;
    }, [])
  },

  navigationMenus = [{ hasPermission: false, name: 'Main', type: 'section', children: [{ hasPermission: true, name: 'Test', type: 'item', link: '/test' }] }, { hasPermission: true, name: 'Master', type: 'section', children: [{ hasPermission: true, name: 'Operator Group', type: 'item', link: '/operator-group' }, { hasPermission: false, name: 'Operation Group', type: 'item', link: '/operation-group' }] }],
  result = filterByPermission(navigationMenus);

console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
pilchard
  • 12,414
  • 5
  • 11
  • 23
  • I am afraid of recursion. Very complex thing. Have u any guideline which help me to understand recursion very well? – Nasir Jul 17 '21 at 17:48
1

Need to use recursion

const navigationMenus = [
  {
    hasPermission: false,
    name: 'Main',
    type: 'section',
    children: [
      {
        hasPermission: true,
        name: 'Test',
        type: 'item',
        link: '/test'
      }
    ]
  },
  {
    hasPermission: true,
    name: 'Master',
    type: 'section',
    children: [
      {
        hasPermission: true,
        name: 'Operator Group1',
        type: 'item',
        link: '/operator-group'
      },
      {
        hasPermission: false,
        name: 'Operation Group2',
        type: 'item',
        link: '/operation-group',
        children: [
          {
            hasPermission: true,
            name: 'Operator Group3',
            type: 'item',
            link: '/operator-group'
          },
          {
            hasPermission: false,
            name: 'Operation Group4',
            type: 'item',
            link: '/operation-group'
          }
    ]
      }
    ]
  }
];

function filterRec(arr = []) {
    return arr.map(el => {
        const elem = el.hasPermission && el
        const children = filterRec(el.children)

        const result = ({ ...elem, ...(children.length && ({children}) || {}) })
        return Object.keys(result).length && result
    }).filter(Boolean)
}

   // or without empty children key
// /function filterRec(arr = []) {
// return arr.reduce((acc, el) => {
//     let result = el.hasPermission && el

//     const children = filterRec(el.children)
//     if (result && children.length) {
//         result.children = children
//     } else if (!result) {
//         result = children
//     }

//     return acc.concat(result).filter(Boolean)
// }, [])
// }
console.log(filterRec(navigationMenus))

Probably answered Recursively filter array of objects

0

You can achieve that in a single pass using .reduce():

const permittedNavigationMenus = navigationMenus.reduce(
  (acc, cur) => {
    if (cur.hasPermission) {
      acc.push({
        ...cur,
        children: cur.children.filter(({ hasPermission }) => hasPermission)
      })
    }

    return acc;
  },
  []
);
Corey
  • 6,612
  • 4
  • 20
  • 28