Javascript 如何在FP JS中从平面列表构建树
我正在学习函数式Javascript,遇到了一个问题。 我有一个平面物体:Javascript 如何在FP JS中从平面列表构建树,javascript,algorithm,functional-programming,Javascript,Algorithm,Functional Programming,我正在学习函数式Javascript,遇到了一个问题。 我有一个平面物体: const data = [ {id: 1, name: "Folder1", parentId: null}, {id: 2, name: "Folder2", parentId: null}, {id: 3, name: "Folder3", parentId: 1},
const data = [
{id: 1, name: "Folder1", parentId: null},
{id: 2, name: "Folder2", parentId: null},
{id: 3, name: "Folder3", parentId: 1},
{id: 4, name: "Folder4", parentId: 2},
{id: 5, name: "Folder5", parentId: 3},
{id: 6, name: "Folder6", parentId: 3}
]
我希望将其转换为这个层次对象,只使用纯函数,不使用fors、ifs和其他“命令式语句”
结果应该是:
[{
id: 1,
name: "Folder1",
parentId: null,
children = [{
id: 3,
name: "Folder3",
parentId: 1,
children = [{
id: 5,
name: "Folder5",
parentId: 3
},
{
id: 6,
name: "Folder6",
parentId: 3
}
]
}]
},
{
id: 2,
name: "Folder2",
parentId: null,
children = [{
id: 4,
name: "Folder4",
parentId: 2
}]
}
]
有什么想法吗?您可以使用递归函数来实现这一点,但您需要使用
reduce
循环数组,并使用if
语句
const arr=[
{id:1,名称:“Folder1”,parentId:null},
{id:2,名称:“Folder2”,parentId:null},
{id:3,名称:“Folder3”,parentId:1},
{id:4,名称:“Folder4”,parentId:2},
{id:5,名称:“Folder5”,parentId:3},
{id:6,名称:“Folder6”,parentId:3}
]
函数构建树(数据、pId){
返回数据.reduce(函数(r,e){
var e=Object.assign({},e);
if(e.parentId==pId){
var children=buildTree(数据,e.id)
if(children.length)e.children=children
r、 推(e)
}
返回r;
}, [])
}
log(buildTree(arr,null))
您可以使用递归函数执行此操作,但需要使用reduce
循环数组,并使用if
语句
const arr=[
{id:1,名称:“Folder1”,parentId:null},
{id:2,名称:“Folder2”,parentId:null},
{id:3,名称:“Folder3”,parentId:1},
{id:4,名称:“Folder4”,parentId:2},
{id:5,名称:“Folder5”,parentId:3},
{id:6,名称:“Folder6”,parentId:3}
]
函数构建树(数据、pId){
返回数据.reduce(函数(r,e){
var e=Object.assign({},e);
if(e.parentId==pId){
var children=buildTree(数据,e.id)
if(children.length)e.children=children
r、 推(e)
}
返回r;
}, [])
}
log(buildTree(arr,null))
这是一个没有if
的提案,但带有和。它需要一个排序数组
var data=[{id:1,name:“Folder1”,parentId:null},{id:2,name:“Folder2”,parentId:null},{id:3,name:“Folder3”,parentId:1},{id:4,name:“Folder4”,parentId:2},{id:5,name:“Folder5”,parentId:3},{id:6,name:“Folder6”,parentId:3},
树=数据
.减少(
(m,a)=>(
M
.get(a.parentId)
.push(Object.assign({},a,{子项:m.set(a.id,[]).get(a.id)}),
M
),
新映射([[null,[]]
)
.get(null);
控制台日志(树)代码>
.as控制台包装{max height:100%!important;top:0;}
这是一个没有if
的提案,但带有和。它需要一个排序数组
var data=[{id:1,name:“Folder1”,parentId:null},{id:2,name:“Folder2”,parentId:null},{id:3,name:“Folder3”,parentId:1},{id:4,name:“Folder4”,parentId:2},{id:5,name:“Folder5”,parentId:3},{id:6,name:“Folder6”,parentId:3},
树=数据
.减少(
(m,a)=>(
M
.get(a.parentId)
.push(Object.assign({},a,{子项:m.set(a.id,[]).get(a.id)}),
M
),
新映射([[null,[]]
)
.get(null);
控制台日志(树)代码>
作为控制台包装{max height:100%!重要;top:0;}
很好,谢谢。但同样,这里有IFs和temp变量。我正在寻找一个纯FP解决方案ifs(或其他答案中的三元运算符)和临时变量有什么问题,只要你有不变性?我不认为有任何问题,OP将此视为一个练习。我也在做更多的工作,努力改变我的风格。例如,三元运算符用作op/noop,这是我在scala中学习到的,通常用于显式设置值。@timconolazio“[trialum]通常用于显式设置值”?-三元运算符形成一个表达式,该表达式可以是任何其他表达式的一部分是。。。?不知道你在说什么。三元运算符的实际用途是,根据这个条件将这个变量设置为这个或那个值。我自己,我从来没有看到它曾经说,“根据这个条件,要么执行这个操作,要么什么都不做。”这总是用命令式的“如果”来执行,作者说他想避免。很好,谢谢。但同样,这里有IFs和temp变量。我正在寻找一个纯FP解决方案ifs(或其他答案中的三元运算符)和临时变量有什么问题,只要你有不变性?我不认为有任何问题,OP将此视为一个练习。我也在做更多的工作,努力改变我的风格。例如,三元运算符用作op/noop,这是我在scala中学习到的,通常用于显式设置值。@timconolazio“[trialum]通常用于显式设置值”?-三元运算符形成一个表达式,该表达式可以是任何其他表达式的一部分是。。。?不知道你在说什么。三元运算符的实际用途是,根据这个条件将这个变量设置为这个或那个值。就我自己而言,我从来没有看到过这样一句话:“基于这个条件,要么执行这个操作,要么什么都不做。”这句话总是用命令式的“如果”来执行,作者说他想避免这个命令。if
本身并不是一件坏事。但是,在Javascript中,它是作为语句实现的,并且语句不返回任何内容。但当您在函数中使用它时,通常是可以的。如果
本身不是一件坏事。但是,在Javascript中,它是作为语句实现的,并且语句不返回任何内容。但是当你在一个函数中使用它时,它通常是可以的。编辑(因为我喝了一些浓咖啡)来蹦床,所以递归不会填满堆栈。应该可以用这种方法做一棵相当大的树。有朝一日,当尾部递归得到全面实现时,要小心
const data = [
{id: 1, name: "Folder1", parentId: null},
{id: 2, name: "Folder2", parentId: null},
{id: 3, name: "Folder3", parentId: 1},
{id: 4, name: "Folder4", parentId: 2},
{id: 5, name: "Folder5", parentId: 3},
{id: 6, name: "Folder6", parentId: 3}
];
function trampoline ( f ) {
while ( f && f instanceof Function ) { f = f ( ); }
return f;
}
function buildTree ( data, copy, top = [] ) {
function recur ( data, copy, top ) {
copy = copy || data.concat ( [] );
let current = copy.shift ( );
current ? doWork ( ) : null;
function doWork ( ) {
top = top.concat ( ( ! current.parentId ? current : [] ) );
current.children = copy.filter ( x => { return current.id === x.parentId } );
}
return ( current ? recur.bind ( null, data, copy, top ) : top );
}
return trampoline ( recur.bind ( null, data, copy, top ) );
}
data.map ( x => { x [ 'children' ] = [ ]; return x; } );
console.log ( buildTree ( data ) );