[英]Removing items from a nested array with unknown depth
I am trying to remove items from a nested array based on an array of correct matches.我正在尝试根据正确匹配的数组从嵌套数组中删除项目。
Three requirements apply:适用三个要求:
I have build a function to recursively get to the deepest level and filter the items based on the $match array.我已经构建了一个函数来递归地到达最深层次并根据 $match 数组过滤项目。
This is what my code looks like so far:这是我的代码到目前为止的样子:
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"
}
]
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));
};
I have updated filterRecursively.我已经更新了过滤器递归。
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' } // ] // }] // }];
I think it's better to separate out a generic node filtering technique that handles children appropriately from the code that checks the names.我认为最好从检查名称的代码中分离出适当处理子节点的通用节点过滤技术。 Here filterNodes
accepts a predicate that says whether the node should be included (without worrying about children).这里filterNodes
接受一个谓词,表示是否应该包含节点(不用担心子节点)。 It then does the child handling bit.然后它会处理孩子。
We write our main function by just passing a predicate that tests whether the name is on the allowed list.我们只通过传递一个谓词来测试名称是否在允许列表中来编写我们的 main 函数。
Together, it looks like this:在一起,它看起来像这样:
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}
Because the forEach
basically "skips" layers without returning anything, you end up with just your first and deepest result.因为forEach
基本上“跳过”层而不返回任何内容,所以您最终只会得到第一个也是最深的结果。
I also think your function is a bit more complicated because it starts with an array, rather than a sort of ROOT
node.我还认为您的函数有点复杂,因为它以数组开头,而不是某种ROOT
节点。
Here's an alternative that (I think) meets your requirements:这是(我认为)满足您要求的替代方案:
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
}]
}
In a runnable snippet:在一个可运行的片段中:
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);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.