Javascript 如何在FP JS中从平面列表构建树

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},

我正在学习函数式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}
]
我希望将其转换为这个层次对象,只使用纯函数,不使用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 ) );