如何遍历树结构并只返回某些值?



标题说明了一切,我目前正在努力解决这个问题,在这个问题中,我有一个数组,它是这样的:

What I have

[
{
"label": "X-1",
"checked": false,
"value": 1,
"children": [
{
"label": "X-1-1",
"checked": false,
"value": 11,
"isOperation": true,
"children": []
},
{
"label": "X-1-2",
"checked": false,
"value": 12,
"isOperation": true,
"children": []
},
{
"label": "X-1-3",
"checked": false,
"value": 13,
"children": [
{
"label": "X-1-3-1",
"checked": false,
"value": 131,
"isOperation": true,
"children": []
},
{
"label": "X-1-3-2",
"checked": true,
"value": 132,
"isOperation": true,
"children": []
},
{
"label": "X-1-3-3",
"checked": false,
"value": 1313,
"children": []
}
]
}
]
},
{
"label": "X-2",
"checked": true,
"value": 2,
"children": [
{
"label": "X-2-1",
"checked": true,
"value": 21,
"children": []
}
]
}
]

,我只能返回

表示checked: true

的值,结构如下:我要返回的

[
{
"TransactionId": 1312,
"Description": "X-1-3-2",
"TransactionChild": []
},
{
"TransactionId": 2,
"Description": "X-2",
"TransactionChild": [
{
"TransactionId": "21",
"Description": "X-2-1"
}
]
}
]

作为请求,我已经得到了什么

const convertTreeDataToTransactionSelected = (node: TreeNodeProps[]): TreeItem[] => {
let treeData: TreeItem[] = [];
if (node.length > 1) {
treeData = node.map(el => {
return {
Description: el.label,
TransactionId: Number(el.value),
TransactionChild: el.children ? convertTreeDataToTransactionSelected(el.children) : [],
};
});
}
return treeData;
};

我怎么能这样做呢?(是的,我知道最好的方法是使用递归,但我未能获得所需的返回value)

请记住,如果检查了所有children,父级也检查了(同样的,如果检查了parent,这意味着所有children也检查了)

您可以使用两个函数获取嵌套节点,另一个函数获取与所需约束匹配的根或开始节点。

要更改属性,您可以添加一个包装器,但这并不重要,重要的是要获得整体功能。

const
getNestedNodes = (children, cb) => q => {
if (cb(q)) {
return [{ ...q, children: q.children.flatMap(getNestedNodes(children, cb)) }]
} else {
children.push(...getNodes(q.children, cb));
return [];
}
},
getNodes = (data, cb) => data.flatMap(o => {
if (cb(o)) {
const children = [];
return [
{ ...o, children: o.children.flatMap(getNestedNodes(children, cb)) },
...children
];
} else {
return getNodes(o.children, cb);
}
}),
data = [{ label: "X-1", checked: false, value: 1, children: [{ label: "X-1-1", checked: false, value: 11, isOperation: true, children: [] }, { label: "X-1-2", checked: false, value: 12, isOperation: true, children: [] }, { label: "X-1-3", checked: false, value: 13, children: [{ label: "X-1-3-1", checked: false, value: 131, isOperation: true, children: [] }, { label: "X-1-3-2", checked: true, value: 132, isOperation: true, children: [] }, { label: "X-1-3-3", checked: false, value: 1313, children: [{ label: "X-1-3-3-1", checked: true, value: 1331, children: [] }] }] }] }, { label: "X-2", checked: true, value: 2, children: [{ label: "X-2-1", checked: true, value: 21, children: [{ label: "X-2-1-1", checked: false, value: 211, children: [{ label: "X-2-1-1-1", checked: true, value: 2111, children: [] }] }] }] }],
result = getNodes(data, ({ checked }) => checked);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

既然您已经接受了Nina Scholz的答案,我假设输出格式就是您想要的,即树中匹配的所有元素作为平面数组返回。

但是如果你感兴趣,这里有另一种形式,它保持了树结构的完整,返回所有匹配的节点以及它们的所有祖先。

const deepFilter = (pred) => (xs) =>
xs .flatMap (({children = [], ...rest}, _, __, kids = deepFilter (pred) (children)) =>
pred (rest) || kids.length
? [{...rest, ...(kids.length ? {children: kids} : {})}]
: []
)
const keepChecked = deepFilter (x => x .checked)
const input = [{label: "X-1", checked: false, value: 1, children: [{label: "X-1-1", checked: false, value: 11, isOperation: true, children: []}, {label: "X-1-2", checked: false, value: 12, isOperation: true, children: []}, {label: "X-1-3", checked: false, value: 13, children: [{label: "X-1-3-1", checked: false, value: 131, isOperation: true, children: []}, {label: "X-1-3-2", checked: true, value: 132, isOperation: true, children: []}, {label: "X-1-3-3", checked: false, value: 1313, children: []}]}]}, {label: "X-2", checked: true, value: 2, children: [{label: "X-2-1", checked: true, value: 21, children: []}]}]
console .log (keepChecked (input))
.as-console-wrapper {max-height: 100% !important; top: 0}

我们从一个通用的树过滤函数开始,它具有基于任意谓词的这种行为,然后将我们的主函数写成一个简单的调用,传递一个谓词来测试值是否为checked


同样不清楚的是,你是否真的必须将样本输入结构转换为目标输出,或者该样本是否是问题的简化,而没有应用于输出。

如果您确实需要进行转换(即从value,labelchildrenTransactionId,DescriptionTransactionChild),我们可以使用专用的filterMap函数,它一次完成过滤和映射

const deepFilterMap = (pred, transform) => (xs) =>
xs .flatMap (({children = [], ...rest}, _, __, kids = deepFilterMap (pred, transform) (children)) =>
pred (rest) || kids.length
? [transform (rest, kids)]
: []
)
const convert = deepFilterMap (
x => x .checked,
({label, value}, children) => ({
TransactionId: value,
Description: label,
... (children .length ? {TransactionChild: children} : {})
})
)
const input = [{label: "X-1", checked: false, value: 1, children: [{label: "X-1-1", checked: false, value: 11, isOperation: true, children: []}, {label: "X-1-2", checked: false, value: 12, isOperation: true, children: []}, {label: "X-1-3", checked: false, value: 13, children: [{label: "X-1-3-1", checked: false, value: 131, isOperation: true, children: []}, {label: "X-1-3-2", checked: true, value: 132, isOperation: true, children: []}, {label: "X-1-3-3", checked: false, value: 1313, children: []}]}]}, {label: "X-2", checked: true, value: 2, children: [{label: "X-2-1", checked: true, value: 21, children: []}]}]
console .log (convert (input))
.as-console-wrapper {max-height: 100% !important; top: 0}

当然,我们也可以写一个deepMap函数,必要时我们可以把它和deepFilter结合起来。像这样的代码可能会做:

const deepMap = (transform) => (xs) =>
xs .map (({children = [], ...rest}, _, __, kids = deepMap (transform) (children)) => transform ({
... rest, 
... (kids.length ? {children: kids} : {})
}))

最新更新