首页 > 解决方案 > 按特定属性值嵌套的对象的递归过滤数组

问题描述

这是我得到的数组,我的目的是过滤整个对象数组并返回一个具有确定名称的对象数组(即使它位于嵌套的最深层次的子对象中)。例如,如果我按“Model8”过滤,我的函数的返回值必须是 =[{ name: "Model8", type: "file", path: "/path/to/file" }]

const arr = [
  {
    name: "Model",
    type: "directory",
    path: "/path/to/folder",
    children: [
      {
        name: "Model1",
        type: "file",
        path: "/path/to/file",
        children: [
          {
            name: "Model2",
            type: "file",
            path: "/path/to/file",
            children: [
              {
                name: "Model3",
                type: "file",
                path: "/path/to/file",
                children: [
                  { name: "Model4", type: "file", path: "/path/to/file" },
                ],
              },
            ],
          },
        ],
      },
    ],
  },
  {
    name: "Inventory",
    type: "directory",
    path: "/path/to/folder",
    children: [{ name: "inventory.yaml", type: "file", path: "/path/to/file" }],
  },
  {
    name: "UI",
    type: "directory",
    path: "/path/to/folder",
    children: [
      { name: "elements", type: "directory", path: "/path/to/file" },
      { name: "viewmodel", type: "directory", path: "/path/to/file" },
      { name: "i18n", type: "directory", path: "/path/to/file" },
      {
        name: "index.template.html",
        type: "file",
        path: "/path/to/file",
        children: [
          {
            name: "Model5",
            type: "file",
            path: "/path/to/file",
            children: [
              {
                name: "Model6",
                type: "file",
                path: "/path/to/file",
                children: [
                  {
                    name: "Model7",
                    type: "file",
                    path: "/path/to/file",
                    children: [
                      { name: "Model8", type: "file", path: "/path/to/file" },
                    ],
                  },
                ],
              },
            ],
          },
        ],
      },
    ],
  },
  { name: "DeviceConnector", type: "directory", children: [] },
];

我想出了2个选项:

1)

function searchFilter(searchVal,arr) {
        const res = arr.filter(function filteredList(el) {
          if (el.children) {
              el.children = el.children.filter(filteredList);
          }
          if (el.name.toLowerCase().includes(searchVal.toLowerCase())) return true;
          return res;
      }); 
    }
    
    searchFilter("Model8",arr)

但是这里的主要问题是由于某种原因我不能“在初始化之前访问'res'”

2)

function searchFilter(arr, name) {
  const searchItem = arr.find((i) => i.name === name);
  if (!searchItem) {
    return arr.filter((i) => searchFilter(i.children, name));
  }
  return searchitem;
}

在这里,我不能低于数组的第一个迭代对象。最大深度是具有“Model3”名称属性的对象。

标签: javascriptarraysrecursionfilter

解决方案


我们可以编写一个通用函数来收集与提供的谓词匹配的所有嵌套值,然后在它上面进行名称搜索,如下所示:

const collect = (pred) => (xs = []) => 
  xs .flatMap (x => [
    ... (pred (x) ? [x] : []),
    ... collect (pred) (x .children)
  ])

const findByName = (target) => 
  collect (({name}) => name == target)

const arr = [{name: "Model", type: "directory", path: "/path/to/folder", children: [{name: "Model1", type: "file", path: "/path/to/file", children: [{name: "Model2", type: "file", path: "/path/to/file", children: [{name: "Model3", type: "file", path: "/path/to/file", children: [{name: "Model4", type: "file", path: "/path/to/file"}]}]}]}]}, {name: "Inventory", type: "directory", path: "/path/to/folder", children: [{name: "inventory.yaml", type: "file", path: "/path/to/file"}]}, {name: "UI", type: "directory", path: "/path/to/folder", children: [{name: "elements", type: "directory", path: "/path/to/file"}, {name: "viewmodel", type: "directory", path: "/path/to/file"}, {name: "i18n", type: "directory", path: "/path/to/file"}, {name: "index.template.html", type: "file", path: "/path/to/file", children: [{name: "Model5", type: "file", path: "/path/to/file", children: [{name: "Model6", type: "file", path: "/path/to/file", children: [{name: "Model7", type: "file", path: "/path/to/file", children: [{name: "Model8", type: "file", path: "/path/to/file"}]}]}]}]}]}, {name: "DeviceConnector", type: "directory", children: []}]

console .log (findByName ('Model8') (arr))

我们可以在这里传递任何谓词。我们可以进行不区分大小写的比较,或子字符串搜索,或组合。但这与树的实际遍历无关,因此将其分离为自己的函数很有用。

如果你真的不喜欢这样称呼findByName ('Model8') (arr)(我现在更喜欢,但有些人不喜欢),我们可以这样重写:

const findByName = (xs, target) => 
  collect (({name}) => name == target) (xs)

findByName (arr, 'Model8')

如果我们愿意,我们可以更进一步,传递一个函数来决定如何找到节点的子节点。在这里我们只需要传递(x) => x .children,但并不是我们想要使用的每个结构都必须使用这个名称"children"。该版本留给读者作为练习。;-)


推荐阅读