我有一棵树,它将包含的嵌套对象数量有所不同。通用示例如下-
tagClass: Variable
我试图删除所有具有const treeData = {
id: 1,
title: "Group1",
tagClass: "Object",
tagIds: [6]
children: [
{
id: 2,
title: "Group2",
tagClass: "Object",
tagIds: [3,4]
},
{ id: 5, title: "Group3", tagClass: "Object" },
],
};
作为属性的嵌套对象,然后将该对象ID的引用作为其父对象的属性留在数组中-
const recursiveFunc = (treeData) => {
if (treeData.children) {
treeData.children = treeData.children
.filter((child) => child.tagClass === "Object")
.map((child) => recursiveFunc(child));
return treeData;
}
};
const updatedTreeData = recursiveFunc(treeData);
我知道.filter,.map和递归将是此的便捷工具,但是我很快就会遇到困难。非常感谢解决该算法问题的任何帮助。我尝试过的部分内容-
fetch API
感谢能帮助您解决此问题的聪明人。干杯。
答案 0 :(得分:2)
recursiveFunc
方法基于您的想法。在过滤期间创建tagIds
属性。有关详细信息,请参见代码段中的注释。
const treeData = {
id: 1,
title: "Group1",
tagClass: "Object",
children: [
{
id: 2,
title: "Group2",
tagClass: "Object",
children: [
{ id: 3, title: "Tag1", tagClass: "Variable" },
{ id: 4, title: "Tag2", tagClass: "Variable" },
],
},
{ id: 5, title: "Group3", tagClass: "Object" },
{ id: 6, title: "Tag3", tagClass: "Variable" },
],
};
const recursiveFunc = (treeData) => {
if(treeData.children){
//filter treeData.children
const children = treeData.children.filter(child => {
if(child.tagClass === 'Variable'){
//if tagclass is variable we create tagIds property for treeData
treeData.tagIds? treeData.tagIds.push(child.id) : treeData.tagIds = [child.id];
// return false to filter out this child
return false
}
//not varaible tagclass, we go deeper
recursiveFunc(child);
//keep the child
return true
})
//if children is an empty array, delete children property from treeData
children.length === 0 ? delete treeData.children : treeData.children = children
}
return treeData
};
const updatedTreeData = recursiveFunc(treeData);
console.log(updatedTreeData)
答案 1 :(得分:2)
递归数据用于递归程序,用于递归数据
您可以使用inductive reasoning编写递归transform
函数。 transform
接受一个输入o
和一个函数test
,该函数接收一个对象并在(且仅当)应该转换该对象的情况下返回true
。
o
是一个数组,则transform
每个子v
都具有相同的test
o
不是数组。如果输入是对象,并且它通过了test
,则修剪该对象并仅返回对输入的id
的引用o
是一个对象,不传递test
。映射到输入对象,并transform
的每个子值v
具有相同的test
o
是 not 的数组,而 not 是一个对象。输入是一个简单值,例如字符串"foo"
或数字1
。返回未经{transform
-ed的输入。const transform = (o = {}, test = identity) =>
Array.isArray(o)
? o.map(v => transform(v, test)) // 1
: Object(o) === o
? test(o)
? o.id // 2
: objectMap(o, v => transform(v, test)) // 3
: o // 4
将工作分流到objectMap
函数使我们更容易解决问题,并通过使用通用过程促进代码重用-
const identity = x =>
x
const objectMap = (o = {}, f = identity) =>
Object.fromEntries(
Object.entries(o).map(([ k, v ]) => [ k, f(v) ])
)
const example =
objectMap
( { a: 1, b: 2, c: 3, d: 4 }
, x => x * x // <-- square each value
)
console.log(example)
// { a: 1, b: 4, c: 9, d: 16 } // <-- squared
我们像高阶函数一样使用transform
,例如.filter
-
const result =
transform
( treeData // <-- input
, x => x.tagClass === "Variable" // <-- test
)
console.log(result)
输出-
{ id: 1
, title: "Group1"
, tagClass: "Object"
, children:
[ { id: 2
, title: "Group2"
, tagClass: "Object"
, children: [ 3, 4 ] // <-- transformed 3 and 4
}
, { id: 5
, title: "Group3"
, tagClass: "Object"
}
, 6 // <-- transformed 6
]
}
代码沙箱
展开以下代码段,以在您自己的浏览器中验证结果-
const identity = x =>
x
const objectMap = (o = {}, f = identity) =>
Object.fromEntries(
Object.entries(o).map(([ k, v ]) => [ k, f(v) ])
)
const transform = (o = {}, test = identity) =>
Array.isArray(o)
? o.map(v => transform(v, test))
: Object(o) === o
? test(o)
? o.id
: objectMap(o, v => transform(v, test))
: o
const treeData =
{id:1,title:"Group1",tagClass:"Object",children:[{id:2,title:"Group2",tagClass:"Object",children:[{id:3,title:"Tag1",tagClass:"Variable"},{id:4,title:"Tag2",tagClass:"Variable"}]},{id:5,title:"Group3",tagClass:"Object"},{id:6,title:"Tag3",tagClass:"Variable"}]}
const result =
transform
( treeData
, ({ tagClass = "" }) => tagClass === "Variable"
)
console.log(JSON.stringify(result, null, 2))
提高可读性
递归是一种功能继承,因此将递归与功能样式一起使用可产生最佳效果。函数式编程旨在降低复杂性并重用定义良好的泛型函数。我认为以下抽象使transform
更好-
const isArray =
Array.isArray
const isObject = o =>
Object(o) === o
const transform = (o = {}, test = identity) =>
isArray(o)
? o.map(v => transform(v, test)) // 1
: isObject(o) && test(o)
? o.id // 2
: isObject(o)
? objectMap(o, v => transform(v, test)) // 3
: o // 4
const result =
transform
( treeData
, ({ tagClass = "" }) =>
tagClass === "Variable"
)
console.log(result)
程序没有做什么
children
或tagIds
进行假设length
应该使o.id
有点不适。如果我们想在不同情况下以不同的方式塑造结果怎么办?为什么id
转换要一成不变?
通过定义另一个功能参数prune
...
const transform = (o = {}, test = identity, prune = identity) =>
isArray(o)
? o.map(v => transform(v, test, prune)) // <-- pass prune
: isObject(o) && test(o)
? prune(o) // <-- prune!
: isObject(o)
? objectMap(o, v => transform(v, test, prune)) // <-- pass prune
: o
现在,我们可以定义如何transform
在呼叫站点上运行test
并执行prune
-
const result =
transform
( treeData
, ({ tagClass = "" }) =>
tagClass === "Variable" // <-- test
, ({ id = 0, title = "" }) =>
({ id, title }) // <-- return only { id, title }
)
输出-
{ id: 1
, title: "Group1"
, tagClass: "Object"
, children:
[ { id: 2
, title: "Group2"
, tagClass: "Object"
, children:
[ { id: 3, title: "Tag1" } // <-- prune { id, title }
, { id: 4, title: "Tag2" } // <-- prune { id, title }
]
}
, { id: 5
, title: "Group3"
, tagClass: "Object"
}
, { id: 6, title: "Tag3" } // <-- prune { id, title }
]
}
答案 2 :(得分:2)
这是我的方法:
const transform = ({children, ...rest}) => {
const kids = (children || []) .filter (({tagClass}) => tagClass !== 'Variable')
const tags = (children || []) .filter (({tagClass}) => tagClass === 'Variable')
return {
... rest,
... (tags .length ? {tagIds: tags .map (({id}) => id)} : {}),
... (kids .length ? {children: kids .map (transform)} : {})
}
}
const treeData = {id: 1, title: "Group1", tagClass: "Object", children: [{id: 2, title: "Group2", tagClass: "Object", children: [{id: 3, title: "Tag1", tagClass: "Variable"}, {id: 4, title: "Tag2", tagClass: "Variable"}]}, {id: 5, title: "Group3", tagClass: "Object"}, {id: 6, title: "Tag3", tagClass: "Variable"}]}
console .log (
transform (treeData)
)
我们将Variables
与其他变量分开,将变量的id
属性收集到tagIds
中,然后在其余子项上重复使用。可以通过允许的partition
函数
const [tags, kids] =
partition (({tagClass}) => tagClass === 'Variable')) (children)
但是我留给你。
答案 3 :(得分:1)
这是使用递归的就地版本。您可以将父级传递到递归调用中,也可以返回父级,以决定是否应将子级保留在children
数组中,并相应地重新排列父级。
const moveVarIdToParent = root => {
if (root.children) {
const children = root.children.map(e => [e, moveVarIdToParent(e)]);
root.tagIds = children.filter(e => e[1]).map(e => e[0].id);
root.children = children.filter(e => !e[1]).map(e => e[0]);
if (!root.children.length) {
delete root.children;
}
if (!root.tagIds.length) {
delete root.tagIds;
}
}
return root.tagClass === "Variable";
};
const treeData = {
id: 1,
title: "Group1",
tagClass: "Object",
children: [
{
id: 2,
title: "Group2",
tagClass: "Object",
children: [
{ id: 3, title: "Tag1", tagClass: "Variable" },
{ id: 4, title: "Tag2", tagClass: "Variable" },
],
},
{ id: 5, title: "Group3", tagClass: "Object" },
{ id: 6, title: "Tag3", tagClass: "Variable" },
],
};
moveVarIdToParent(treeData);
console.log(treeData);