这段递归扁平tree,为什么有子集的父级push不进去?

我想的应该是有6项,但是它只push了4项,有子集的父项没进去

export function flatten (tree = [], arr = [], childrenKey = 'children') {
  tree.forEach((item) => {
    const children = item[childrenKey]
    children ? flatten(children, arr, childrenKey) : arr.push(item)
  })
  return arr
}

const treeData = [
  { key: '0-0', title: '0-0' },
  {
    key: '0-1',
    title: '0-1',
    children: [
      { key: '0-1-0', title: '0-1-0' },
      {
        key: '0-2-0',
        title: '0-2-0',
        children: [
          { key: '0-2-1', title: '0-2-1' },
          { key: '0-2-2', title: '0-2-2' }
        ]
      }
    ]
  }
]

// 调用
this.tableData = flattensFunc(treeData, [], 'children')

// 输出
[
    {
        "key": "0-0",
        "title": "0-0"
    },
    {
        "key": "0-1-0",
        "title": "0-1-0"
    },
    {
        "key": "0-2-1",
        "title": "0-2-1"
    },
    {
        "key": "0-2-2",
        "title": "0-2-2"
    }
]
阅读 2.8k
5 个回答

不管有没有 children,当前项都需要 push 啊,然后再说,有子集的递归进去

arr.push(item);
children ? flatten(children, arr, childrenKey) : void 0;  // 可以改成 if 语句,我只是懒得改了

对于带 children 的 item,输出会含 children 属性,如果不需要,可以用新对象来过滤掉

下面的代码,注意 forEach 回调的参数采用了解构形式来定义,把 children 剥离掉
tree.forEach(({ [childrenKey]: children, ...item }) => {
    // const children = item[childrenKey];
    arr.push(item);
    children ? flatten(children, arr, childrenKey) : void 0;
});

写法的问题,children ? flatten(children, arr, childrenKey) : arr.push(item)的时候只将无子集的项添加了进去,有子集的父项没有添加。

function flatten(tree = [], arr = [], childrenKey = 'children') {
  tree.forEach(item => {
    const children = item[childrenKey]
    arr.push(item)
    children && flatten(children, arr, childrenKey)
  })
  return arr
}

你这个 三元表达式 相当于

if (children) {
  //有子集的情况
  flatten(children, arr, childrenKey);
} else {
  //没子集的情况
  arr.push(item);
}

你没发现这个遍历中的判断写得有问题吗?那些带有children属性的对象本身并没有被push进去,你要这样改就OK了:

function flatten(tree = [], arr = [], childrenKey = "children") {
    tree.forEach((item) => {
        arr.push(item);
        const children = item[childrenKey];
        children && flatten(children, arr, childrenKey);
    });
    return arr;
}

有个细节要注意,直接push进去的对象是没有经过拷贝的,包括children属性值也包含在内,最好把对象先拷贝下,避免源对象被修改,也方便排除掉不需要的属性:

// 第二个参数作为结果可以直接省略,因为在最顶层的操作中就已经能合并进去了
export function flatten(tree = [], childrenKey = "children") {
    return tree.reduce((ret, item) => {
        let children = (item = { ...item })[childrenKey];
        if (children) {
            children = flatten(children, childrenKey);
            delete item[childrenKey];
        } else {
            children = [];
        }
        return [...ret, item, ...children];
    }, []);
}
this.tableData = flattensFunc(treeData, "children");
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题