介绍
实现数组的flat是一道常见的面试题,那树的flat怎么实现呢?
我也是看了tree-to-list(www.npmjs.com/package/tre…)
的源码,在此总结分享一下。效果就是将Tree上的父子节点都提取出来成一个一级的数组
实现treeToList(arrayTree, 'children'),第一个参数是要处理的树,第二个参数是关键词,根据关键词可以确定父子节点关系
例子
数组树
const arrayTree = [{
name: 'name1',
children: [{
name: 'name3',
children: [{
name: 'name5'
}]
}, {
name: 'name4'
}]
}, {
name: 'name2'
}];
treeToList(arrayTree)
[
{ name: 'name1' },
{ name: 'name3' },
{ name: 'name5' },
{ name: 'name4' },
{ name: 'name2' }
]
对象树
objectTree = {
node1: {
name: 'name1',
tree: {
node3: {
name: 'name3',
tree: {
node2: {
name: 'name5',
key5: 'value5'
}
}
},
node4: { name: 'name4' },
}
},
node2: {
name: 'name2',
key2: 'value2'
}
}
treeToList(objectTree, 'tree')
{
node1: { name: 'name1' },
node3: { name: 'name3' },
node4: { name: 'name4' },
node2: {
name: 'name2',
key5: 'value5',
key2: 'value2'
}
}
分析
当涉及到层级不定的问题,往往要想到两种方法
- 栈的结构
- 递归的思想
函数 _transformStack 的作用:取出树上的节点整理成 {value,key}对象放入stack返回
function _transformStack(tree) {
const stack = [];
if (Array.isArray(tree)) { // array tree
for (let index = 0; index < tree.length; index++) {
const node = tree[index];
stack.push({
value: node,
});
}
} else if (Object.prototype.toString.call(tree) === '[object Object]') { // object tree
for (const key in tree) {
if (Object.prototype.hasOwnProperty.call(tree, key)) {
const node = tree[key];
stack.push({
key,
value: node,
});
}
}
}
return stack;
}
接下来重头戏
function treeToList(tree, key = 'children') {
let list = [];
if (Array.isArray(tree)) { // array tree
list = [];
} else if (Object.prototype.toString.call(tree) === '[object Object]') { // object tree
list = {};
} else { // invalid tree
return list;
}
let stack = _transformStack(tree);
while (stack.length) {
const curStack = stack.shift();
const { key: nodeKey, value: node } = curStack;
if (!node) continue; // invalid node
const item = (nodeKey ? list[nodeKey] : {}) || {};
for (const prop in node) {
if (Object.prototype.hasOwnProperty.call(node, prop)
&& prop !== key) {
item[prop] = node[prop];
}
}
if (nodeKey) { // object
list[nodeKey] = item;
} else { // array
list.push(item);
}
const subTree = node[key] || [];
stack = _transformStack(subTree).concat(stack);
}
return list;
}
分析下过程
- 定义一个list为返回值,如果输入是数组树,list为数组;如果是对象树,list为对象
- 首先通过_transformStack获得
{value,key}组成的一个栈stack - 从栈stack中弹出一个对象,取出value。
- 遍历value,将key以外的属性直接copy进新对象,新对象放入list。value[key] 通过 _transformStack 处理结果合并入栈,继续遍历stack。
- stack为空,处理结束,返回list
总结
整个处理由两个函数完成,主要关注treeToList函数。list储存已展开的节点,stack储存未展开的节点,stack每次弹出一个节点,遇到key属性进入展开后继续放入stack,直到stack为空,树已经展平,返回list。