递归过滤对象数组

Nat*_*wer 35 javascript arrays recursion filter

用这个撞墙,以为我会把它贴在这里以防万一有些灵魂碰到了类似的灵魂.我有一些看起来像这样的数据:

const input = [
  {
    value: 'Miss1',
    children: [
      { value: 'Miss2' },
      { value: 'Hit1', children: [ { value: 'Miss3' } ] }
    ]
  },
  {
    value: 'Miss4',
    children: [
      { value: 'Miss5' },
      { value: 'Miss6', children: [ { value: 'Hit2' } ] }
    ]
  },
  {
    value: 'Miss7',
    children: [
      { value: 'Miss8' },
      { value: 'Miss9', children: [ { value: 'Miss10' } ] }
    ]
  },
  {
    value: 'Hit3',
    children: [
      { value: 'Miss11' },
      { value: 'Miss12', children: [ { value: 'Miss13' } ] }
    ]
  },
  {
    value: 'Miss14',
    children: [
      { value: 'Hit4' },
      { value: 'Miss15', children: [ { value: 'Miss16' } ] }
    ]
  },
];
Run Code Online (Sandbox Code Playgroud)

我不知道在运行时层次结构有多深,即有多少级别的对象将拥有子数组.我稍微简化了示例,实际上我需要将值属性与搜索项数组进行匹配.让我们暂时假设我匹配到哪里value.includes('Hit').

我需要一个返回一个新数组的函数,这样:

  • 在输出对象中不应存在每个没有子项的非匹配对象,或者子层次结构中没有匹配项

  • 应保留包含匹配对象的后代的每个对象

  • 匹配对象的所有后代都应保留

我正在考虑一个'匹配对象'是一个value属性,Hit在这种情况下包含字符串,反之亦然.

输出应如下所示:

const expected = [
  {
    value: 'Miss1',
    children: [
      { value: 'Hit1', children: [ { value: 'Miss3' } ] }
    ]
  },
  {
    value: 'Miss4',
    children: [
      { value: 'Miss6', children: [ { value: 'Hit2' } ] }
    ]
  },
  {
    value: 'Hit3',
    children: [
      { value: 'Miss11' },
      { value: 'Miss12', children: [ { value: 'Miss13' } ] }
    ]
  },
  {
    value: 'Miss14',
    children: [
      { value: 'Hit4' },
    ]
  }
];
Run Code Online (Sandbox Code Playgroud)

非常感谢任何花时间阅读这篇文章的人,如果我先到达那里,我会发布我的解决方案.

小智 42

.filter()正如我在上面的评论中所描述的那样使用和进行递归调用基本上就是你所需要的.您只需要.children在返回之前使用递归调用的结果更新每个属性.

返回值只是.length结果.children集合的值,因此如果至少有一个,则保留对象.

var res = input.filter(function f(o) {
  if (o.value.includes("Hit")) return true

  if (o.children) {
    return (o.children = o.children.filter(f)).length
  }
})
Run Code Online (Sandbox Code Playgroud)

const input = [
  {
    value: 'Miss1',
    children: [
      { value: 'Miss2' },
      { value: 'Hit1', children: [ { value: 'Miss3' } ] }
    ]
  },
  {
    value: 'Miss4',
    children: [
      { value: 'Miss5' },
      { value: 'Miss6', children: [ { value: 'Hit2' } ] }
    ]
  },
  {
    value: 'Miss7',
    children: [
      { value: 'Miss8' },
      { value: 'Miss9', children: [ { value: 'Miss10' } ] }
    ]
  },
  {
    value: 'Hit3',
    children: [
      { value: 'Miss11' },
      { value: 'Miss12', children: [ { value: 'Miss13' } ] }
    ]
  },
  {
    value: 'Miss14',
    children: [
      { value: 'Hit4' },
      { value: 'Miss15', children: [ { value: 'Miss16' } ] }
    ]
  },
];

var res = input.filter(function f(o) {
  if (o.value.includes("Hit")) return true

  if (o.children) {
    return (o.children = o.children.filter(f)).length
  }
})
console.log(JSON.stringify(res, null, 2))
Run Code Online (Sandbox Code Playgroud)


请注意,.includes()在String上是ES7,因此可能需要为旧版浏览器打补丁.您可以使用传统.indexOf("Hit") != -1的替代品.


若要不改变原始内容,请创建一个复制对象的地图函数,并在过滤器之前使用该函数.

function copy(o) {
  return Object.assign({}, o)
}

var res = input.map(copy).filter(function f(o) {
  if (o.value.includes("Hit")) return true

  if (o.children) {
    return (o.children = o.children.map(copy).filter(f)).length
  }
})
Run Code Online (Sandbox Code Playgroud)

要真正压缩代码,你可以这样做:

var res = input.filter(function f(o) {
  return o.value.includes("Hit") ||
         o.children && (o.children = o.children.filter(f)).length
})
Run Code Online (Sandbox Code Playgroud)

虽然它有点难以阅读.

  • 它改变了原始“输入”的内容。您可能首先想使用 JSON.parse(JSON.stringify(input)) 制作副本。另外,我感觉这是服务器端JS。 (3认同)

小智 7

这是一个功能,可以满足您的需求。本质上,它将测试其中的每个项目arr是否匹配,然后递归调用其上的filter children。也Object.assign用于不改变基础对象的情况。

function filter(arr, term) {
    var matches = [];
    if (!Array.isArray(arr)) return matches;

    arr.forEach(function(i) {
        if (i.value.includes(term)) {
            matches.push(i);
        } else {
            let childResults = filter(i.children, term);
            if (childResults.length)
                matches.push(Object.assign({}, i, { children: childResults }));
        }
    })

    return matches;
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,这种方法实际上使我更清楚了我要去哪里 (2认同)