从深度未知的嵌套数组中移除项



我试图根据正确匹配的数组从嵌套数组中删除项。

三个要求:

  1. 数组的深度未知。项可以有嵌套的子项。
  2. 只删除没有子条目的条目
  3. 如果项目不在匹配数组中,则应删除

我已经构建了一个函数来递归地到达最深层,并根据$match数组过滤条目。

我的代码是这样的:

import * as lodash from "https://cdn.skypack.dev/lodash@4.17.21";
let filterRecursively = (arr, match) => {
// Recursively go to the deepest array we can find
arr.forEach(el => {
arr = el.children ? filterRecursively(el.children, match) : arr
});
// If we are at the deepest level we filter the items ...
if (arr[0] && arr[0].children === undefined) {
return _.filter(arr, (item) => {
return match.includes(item.name)
})
} else { // ... if not we just return the array as-is
return arr
}
}
let arr = [
{
'name': 'John',
'children': [
{
'name': 'John',
'children': [
{ 'name': 'John' },
{ 'name': 'Jane' },
{ 'name': 'Joe' }
]
}]
}, {
'name': 'Jeff',
'children': [
{
'name': 'Joe',
'children': [
{ 'name': 'Jill' },
{ 'name': 'Jeff' },
{ 'name': 'Joe' }
]
}]
}];
let match = ['John', 'Joe'];
let result = filterRecursively(arr, match);
console.log(result);
// Expected result:
[
{
'name': 'John',
'children': [
{
'name': 'John',
'children': [
{ 'name': 'John' },
{ 'name': 'Joe' }
]
}]
}, {
'name': 'Jeff',
'children': [
{
'name': 'Joe',
'children': [
{ 'name': 'Joe' }
]
}]
}];
// Current output
[
{
"name": "Joe"
}
]

参见Codepen

因为forEach基本上"跳过";层而不返回任何东西,你最终只得到第一个和最深的结果。

我也认为你的函数有点复杂,因为它从一个数组开始,而不是一种ROOT节点。

这里有一个(我认为)符合你要求的替代方案:

let childlessMatch = (node, match) => {
// If it's at the deepest level, check against match
if (node.children === undefined) {
return match.includes(node.name) ? [node] : [];
}

// If not, calculate the next child layer first
const newChildren = node.children.flatMap(c => childlessMatch(c, match));
// With the children calculated, we can prune based on whether there
// are any children left to show
if (newChildren.length === 0) return [];

return [{
...node,
children: newChildren
}]
} 

在可运行代码段中:

let childlessMatch = (node, match) => {
if (node.children === undefined) {
return match.includes(node.name) ? [node] : [];
}

const newChildren = node.children.flatMap(c => childlessMatch(c, match));
if (newChildren.length === 0) return [];

return {
...node,
children: newChildren
}
}
let arr = [
{
'name': 'John',
'children': [
{
'name': 'John',
'children': [
{ 'name': 'John' },
{ 'name': 'Jane' },
{ 'name': 'Joe' }
]
}]
}, {
'name': 'Jeff',
'children': [
{
'name': 'Joe',
'children': [
{ 'name': 'Jill' },
{ 'name': 'Jeff' },
{ 'name': 'Joe' }
]
}]
}];
let match = ['John', 'Joe'];
let result = childlessMatch({ children: arr }, match).children;
console.log(result);

我认为最好将适当处理子节点的通用节点过滤技术从检查名称的代码中分离出来。这里,filterNodes接受一个谓词,该谓词表示是否应该包含该节点(而不必担心子节点)。然后执行子处理位。

我们通过传递一个谓词来编写main函数,该谓词用于测试名称是否在允许的列表中。

合起来是这样的:

const filterNodes = (pred) => (nodes) => 
nodes .flatMap (
(node, _, __, 
kids = filterNodes (pred) (node .children || [])
) => pred (node) || node .children ?.length > 0 
? [{...node, ... (kids .length ? {children: kids} : {})}] 
: []
)
const removeUnmatchedNames = (names) =>
filterNodes (({name}) => names .includes (name))

const arr = [{name: "John", children: [{name: "John", children: [{name: "John"}, {name: "Jane"}, {name: "Joe"}]}]}, {name: "Jeff", children: [{name: "Joe", children: [{name: "Jill"}, {name: "Jeff"}, {name: "Joe"}]}]}]
console .log (removeUnmatchedNames (['John', 'Joe']) (arr))
.as-console-wrapper {max-height: 100% !important; top: 0}

let filterRecursively = (arr, match) => {
// Recursively go to the deepest array we can find
return arr
.map((el) =>
el.children
? { ...el, children: filterRecursively(el.children, match) }
: el
)
.filter((el) => el.children || match.includes(el.name));
};

我已经递归更新了filterrecursive .

let filterRecursively = (arr, match) => {
// Recursively go to the deepest array we can find
return arr
.map((el) =>
el.children
? { ...el, children: filterRecursively(el.children, match) }
: el
)
.filter((el) => el.children || match.includes(el.name));
};

let arr = [
{
name: "John",
children: [
{
name: "John",
children: [{ name: "John" }, { name: "Jane" }, { name: "Joe" }],
},
],
},
{
name: "Jeff",
children: [
{
name: "Joe",
children: [{ name: "Jill" }, { name: "Jeff" }, { name: "Joe" }],
},
],
},
];

let match = ["John", "Joe"];
let result = filterRecursively(arr, match);

console.log(JSON.stringify(result));

// Expected result:
// [
//   {
//     'name': 'John',
//     'children': [
//       {
//         'name': 'John',
//         'children': [
//           { 'name': 'John' },
//           { 'name': 'Joe' }
//         ]
//       }]
//   }, {
//     'name': 'Jeff',
//     'children': [
//       {
//         'name': 'Joe',
//         'children': [
//           { 'name': 'Joe' }
//         ]
//       }]
//   }];

最新更新