递归过滤对象数组
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了递归过滤对象数组相关的知识,希望对你有一定的参考价值。
用这个撞墙,以为我会把它贴在这里以防万一有些灵魂碰到了类似的灵魂。我有一些看起来像这样的数据:
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')
。
我需要一个返回一个新数组的函数,这样:
- 在输出对象中不应存在每个没有子项的非匹配对象,或者子层次结构中没有匹配项
- 应保留包含匹配对象的后代的每个对象
- 匹配对象的所有后代都应保留
我正在考虑一个'匹配对象'是一个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' },
]
}
];
非常感谢任何花时间阅读这篇文章的人,如果我先到达那里,我会发布我的解决方案。
正如我在上面的评论中描述的那样使用.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
}
})
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))
这是一个能够满足您需求的功能。基本上它会测试arr
中的每个项目的匹配,然后递归调用其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;
}
我认为这将是一个递归的解决方案。这是我试过的一个。
function find(obj, key) {
if (obj.value && obj.value.indexOf(key) > -1){
return true;
}
if (obj.children && obj.children.length > 0){
return obj.children.reduce(function(obj1, obj2){
return find(obj1, key) || find(obj2, key);
}, {});
}
return false;
}
var output = input.filter(function(obj){
return find(obj, 'Hit');
});
console.log('Result', output);
或者,您可以使用_.filterDeep
扩展中的deepdash方法来获取lodash
:
var keyword = 'Hit';
var foundHit = _.filterDeep(
input,
function(value) {
return value.value.includes(keyword);
},
{
tree: true,
onTrue: { skipChildren: true },
}
);
这是你的情况的full test
以上是关于递归过滤对象数组的主要内容,如果未能解决你的问题,请参考以下文章
NC41 最长无重复子数组/NC133链表的奇偶重排/NC116把数字翻译成字符串/NC135 股票交易的最大收益/NC126换钱的最少货币数/NC45实现二叉树先序,中序和后序遍历(递归)(代码片段