【问题标题】:Filter a JavaScript deeply nested array of objects based on its values根据值过滤 JavaScript 深度嵌套的对象数组
【发布时间】:2019-08-10 10:04:58
【问题描述】:

我的菜单结构如下:

const menu = [
  {
    title: 'Supervisor Dashboard',
    link: '/dashboard/supervisor-dashboard',
    slug: '/dashboard/supervisor-dashboard'
  },
  {
    title: 'User Dashboard',
    link: '/dashboard/user-dashboard',
    slug: '/dashboard/user-dashboard'
  },
  {
    title: 'Inventory',
    slug: '/inventory',
    children: [
      {
        title: 'Add Inventory',
        link: '/inventory/add-inventory',
        slug: '/inventory/add-inventory'
      },
      {
        title: 'Remove Inventory',
        link: '/inventory/remove-inventory',
        slug: '/inventory/remove-inventory'
      },
    ]
  },
  {
    title: 'Membership',
    slug: '/membership',
    children: [
      {
        title: 'Program A',
        slug: '/membership/program-a',
        children: [
          {
            title: 'View Membership',
            link: '/membership/program-a/view',
            slug: '/membership/program-a/view'
          },
          {
            title: 'Add Membership',
            link: '/membership/program-a/add',
            slug: '/membership/program-a/add'
          },
          {
            title: 'Delete Membership',
            link: '/membership/program-a/delete',
            slug: '/membership/program-a/delete'
          }
        ]
      },
      {
        title: 'Program B',
        slug: '/membership/program-b',
        children: [
          {
            title: 'View Membership',
            link: '/membership/program-b/view',
            slug: '/membership/program-b/view'
          },
          {
            title: 'Add Membership',
            link: '/membership/program-b/add',
            slug: '/membership/program-b/add'
          },
          {
            title: 'Delete Membership',
            link: '/membership/program-b/delete',
            slug: '/membership/program-b/delete'
          }
        ]
      }
    ],
  },
];

我想过滤菜单,即只显示授予用户的任何内容。用户只能根据允许的 slug 查看菜单,如下所示:

const allowed_slug = [
  '/dashboard/user-dashboard',
  '/inventory/add-inventory',
  '/membership/program-b/view',
  '/membership/program-b/add'
];

使用.filter 我可以过滤数组的第一层。以下是我目前取得的成就:

function filterMenu(menus, allowed_slug) {
  const result = menus.filter(function (menu_item) {
    return allowed_slug.filter(function(slug) {
      return menu_item.slug.indexOf(slug) > -1;
    }).length;
  });

  return result;
}

理想的输出应该是这样的:

o
|-- User Dashboard
|-- Inventory
|   `-- Add Inventory
`-- Membership
    `-- Program B
        |-- View Membership
        `-- Add Membership

问题是,我无法过滤嵌套数组,即孩子和孩子的孩子。很感谢任何形式的帮助。 :)

【问题讨论】:

  • 首先使用some()every() 而不是filter().length。另外我很确定您正在寻找menu_item.slug.indexOf(slug) == 0(不是>= 0)或menu_item.slug.startsWith(slug)
  • 为什么仪表板没有缩进?
  • @NinaScholz 这就是结构。我想添加一些动态结构。
  • @Bergi 仍然无法解决嵌套过滤问题。我已经尝试了一天多结合多个数组原型函数。
  • @NinaScholz 实际上,它与您在 stackoverflow.com/a/45482594 中的答案非常相似。只是结构更加嵌套。

标签: javascript arrays filtering


【解决方案1】:

尝试使用此功能,看看它是否符合您的需求。它基本上使用递归 reduce 函数创建一个新数组,检查每个元素是否符合 slugs 数组的标准:

const menu = [ { title: 'Supervisor Dashboard', link: '/dashboard/supervisor-dashboard', slug: '/dashboard/supervisor-dashboard' }, { title: 'User Dashboard', link: '/dashboard/user-dashboard', slug: '/dashboard/user-dashboard' }, { title: 'Inventory', slug: '/inventory', children: [ { title: 'Add Inventory', link: '/inventory/add-inventory', slug: '/inventory/add-inventory' }, { title: 'Remove Inventory', link: '/inventory/remove-inventory', slug: '/inventory/remove-inventory' }, ] }, { title: 'Membership', slug: '/membership', children: [ { title: 'Program A', slug: '/membership/program-a', children: [ { title: 'View Membership', link: '/membership/program-a/view', slug: '/membership/program-a/view' }, { title: 'Add Membership', link: '/membership/program-a/add', slug: '/membership/program-a/add' }, { title: 'Delete Membership', link: '/membership/program-a/delete', slug: '/membership/program-a/delete' } ] }, { title: 'Program B', slug: '/membership/program-b', children: [ { title: 'View Membership', link: '/membership/program-b/view', slug: '/membership/program-b/view' }, { title: 'Add Membership', link: '/membership/program-b/add', slug: '/membership/program-b/add' }, { title: 'Delete Membership', link: '/membership/program-b/delete', slug: '/membership/program-b/delete' } ] } ], }, ]; const allowed_slug = [ '/dashboard/user-dashboard', '/inventory/add-inventory', '/membership/program-b/view', '/membership/program-b/add' ];

const filterMenu = (menu, allowed) =>
    menu.reduce((a, {title, link, slug, children = []}) =>
        (children = filterMenu(children, allowed), (children.length && (a = [...a, {title, slug, children}])) || (allowed.includes(slug) && (a = [...a, {title, link, slug}])), a), []);

console.log(filterMenu(menu, allowed_slug));

更易读的函数版本:

const filterMenu = (menu, allowed) =>
    menu.reduce((array, {title, link, slug, children = []}) => {
        children = filterMenu(children, allowed);
        if (children.length) {
            array.push({title, slug, children});
        } else if (allowed.includes(slug)) {
            array.push({title, link, slug});
        }
        return array;
    }, []);

【讨论】:

    【解决方案2】:

    递归是你的朋友:

     // smae signature as your function
     const filterMenu = (menus, allowed) => menus
        // first of all, copy & filter recursively
       .map(({ title, slug, link, children }) => ({ title, slug, link, children: children && filterMenu(children, allowed) }))
       // then remove all that don't have allowed children and are not allowed themself
       .filter(it => it.children && it.children.length || allowed.includes(it.slug));
    

    【讨论】:

    • 只需在filterMenu 函数中删除一个额外的括号。这个filterMenu(children), allowed) 应该是filterMenu(children, allowed)
    【解决方案3】:

    您可以检查 allowdSlug 是否以实际对象的 slug 开头。

    var menus = [{ title: 'Supervisor Dashboard', link: '/dashboard/supervisor-dashboard', slug: '/dashboard/supervisor-dashboard' }, { title: 'User Dashboard', link: '/dashboard/user-dashboard', slug: '/dashboard/user-dashboard' }, { title: 'Inventory', slug: '/inventory', children: [{ title: 'Add Inventory', link: '/inventory/add-inventory', slug: '/inventory/add-inventory' }, { title: 'Remove Inventory', link: '/inventory/remove-inventory', slug: '/inventory/remove-inventory' }] }, { title: 'Membership', slug: '/membership', children: [{ title: 'Program A', slug: '/membership/program-a', children: [{ title: 'View Membership', link: '/membership/program-a/view', slug: '/membership/program-a/view' }, { title: 'Add Membership', link: '/membership/program-a/add', slug: '/membership/program-a/add' }, { title: 'Delete Membership', link: '/membership/program-a/delete', slug: '/membership/program-a/delete' }] }, { title: 'Program B', slug: '/membership/program-b', children: [{ title: 'View Membership', link: '/membership/program-b/view', slug: '/membership/program-b/view' }, { title: 'Add Membership', link: '/membership/program-b/add', slug: '/membership/program-b/add' }, { title: 'Delete Membership', link: '/membership/program-b/delete', slug: '/membership/program-b/delete' }] }] }], allowed_slug = ['/dashboard/user-dashboard', '/inventory/add-inventory', '/membership/program-b/view', '/membership/program-b/add'],
        filter = menus => menus
            .filter(({ slug }) => allowed_slug.some(s => s.startsWith(slug)))
            .map(({ title, slug, children = [] }) => {
                children = filter(children);
                return Object.assign({ title, slug }, children.length && { children })
            }),
        result = filter(menus);
    
    console.log(result);
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    【讨论】:

    • 感谢您的回答。它有效但不完全。就像@JonasWilms 的回答一样。它在每个菜单中创建一个空子数组,这破坏了我的样式。
    • 巧妙使用Object.assign ;)
    猜你喜欢
    • 2022-01-20
    • 2019-01-29
    • 1970-01-01
    • 2017-06-29
    • 1970-01-01
    • 2020-03-11
    • 1970-01-01
    • 2020-09-24
    • 1970-01-01
    相关资源
    最近更新 更多