我正在学习功能性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
}]
}
]
任何想法?
答案 0 :(得分:4)
此提案没有if
,但有Array#reduce
和Map
。它需要一个排序数组。
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 }],
tree = data
.reduce(
(m, a) => (
m
.get(a.parentId)
.push(Object.assign({}, a, { children: m.set(a.id, []).get(a.id) })),
m
),
new Map([[null, []]])
)
.get(null);
console.log(tree);

.as-console-wrapper { max-height: 100% !important; top: 0; }

或者与上面使用ES2015解构分配相同。它需要一个排序数组,也取决于只有id
,name
和parentId
键的输入数据。
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 }],
tree = data
.reduce(
(m, {id, name, parentId}) => (
m
.get(parentId)
.push({id, name, parentId, children: m.set(id, []).get(id) }),
m
),
new Map([[null, []]])
)
.get(null);
console.log(tree);

.as-console-wrapper { max-height: 100% !important; top: 0; }

当然这可能应该写成可重复使用的功能......
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 }];
// pure, reusable function
var buildTree = (data) =>
data.reduce(
(m, {id, name, parentId}) => (
m
.get(parentId)
.push({id, name, parentId, children: m.set(id, []).get(id) }),
m
),
new Map([[null, []]])
)
.get(null);
console.log(buildTree(data));

.as-console-wrapper { max-height: 100% !important; top: 0; }

最后,如果数据以未排序的顺序到达,我们可以使用自定义比较器处理排序
// unsorted data example
var data = [{ id: 6, name: "Folder6", parentId: 3 }, { 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: 1, name: "Folder1", parentId: null }];
// immutable sort
var sort = (f,xs) => [...xs.sort(f)];
// custom tree comparator
var treeComparator = (x,y) =>
x.parentId - y.parentId || x.id - y.id;
// sort data, then reduce
var buildTree = (data) =>
sort(treeComparator, data).reduce(
(m, {id, name, parentId}) => (
m
.get(parentId)
.push({id, name, parentId, children: m.set(id, []).get(id) }),
m
),
new Map([[null, []]])
)
.get(null);
console.log(buildTree(data));

.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 1 :(得分:0)
您可以使用递归函数执行此操作,但需要使用reduce
循环数组并使用if
语句。
const arr = [
{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 buildTree(data, pId) {
return data.reduce(function(r, e) {
var e = Object.assign({}, e);
if (e.parentId == pId) {
var children = buildTree(data, e.id)
if (children.length) e.children = children
r.push(e)
}
return r;
}, [])
}
console.log(buildTree(arr, null))
答案 2 :(得分:0)
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 ) );