用这个打墙,以为我会把它贴在这里,以防某种灵魂碰到一个类似的灵魂.我有一些数据看起来像这样:
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' } ] } ] },];
我不知道在运行时层次结构将会有多深,即有多少级对象将有一个子数组.我已经简化了这个例子,实际上我需要将值属性与搜索项的数组进行匹配.我们现在假设我在匹配那个value.includes(‘Hit’).
我需要一个返回一个新数组的函数:
>每个不具有子项的非匹配对象,或者子层次结构中没有匹配,不应该存在于输出对象中
>具有包含匹配对象的后代的每个对象都应保留
>匹配对象的所有后代应保留
在这种情况下,我正在考虑一个“匹配对象”为具有值属性的值,并且反之亦然.
输出应该如下所示:
const expected = [ { value: 'Miss1',children: [ { value: 'Hit1',children: [ { value: 'Miss6',] } ];
解决方法
使用.filter()和进行递归调用,就像上面的评论所描述的,基本上是你需要的.您只需要在返回之前更新每个.children属性与递归调用的结果.
返回值只是生成的.children集合的.length,所以如果至少有一个,则保留对象.
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 } })
const input = [ { value: 'Miss1',]; 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))
请注意,一个String上的.includes()是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 } })
要真正压缩代码,可以这样做:
var res = input.filter(function f(o) { return o.value.includes("Hit") || o.children && (o.children = o.children.filter(f)).length })
虽然它有点难以阅读.