【问题标题】:How to flatten tree structure into array of arrays如何将树结构展平为数组数组
【发布时间】:2018-01-30 14:42:26
【问题描述】:

给定这样的结构:

var node = { children: [] }

也就是说:

  1. 它只有children 属性。
  2. 没有parent 属性。
  3. 没有nextSibling 属性。

如何使用自上而下的方法构建包含所有叶节点路径的平面数组列表:

  1. 该算法不使用任何辅助方法,只使用纯 JavaScript。
  2. 算法在从顶部遍历树时构建数组。

所以这里是一个示例数据:

var node = {
  item: 1,
  children: [
    {
      item: 2,
      children: [
        {
          item: 3,
          children: [
            {
              item: 4,
              children: []
            },
            {
              item: 5,
              children: []
            },
            {
              item: 6,
              children: [
                {
                  item: 7,
                  children: []
                },
                {
                  item: 8,
                  children: []
                },
                {
                  item: 9,
                  children: []
                }
              ]
            }
          ]
        },
        {
          item: 10,
          children: [
            {
              item: 11,
              children: []
            },
            {
              item: 12,
              children: [
                {
                  item: 13,
                  children: []
                },
                {
                  item: 14,
                  children: []
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

并且函数应该返回:

[
  [1, 2, 3, 4],
  [1, 2, 3, 5],
  [1, 2, 3, 6, 7],
  [1, 2, 3, 6, 8],
  [1, 2, 3, 6, 9],
  [1, 2, 10, 11],
  [1, 2, 10, 12, 13],
  [1, 2, 10, 12, 14]
]

到目前为止,我的尝试是:

function aggregateNodes(node) {
  var stack = [node]
  var array = []
  while (stack.length) {
    var node = stack.pop()
    array.push(node.item)
    node.children.forEach(function(child){
      stack.push(child)
    })
  }
  return array
}

function aggregateNodesRecursive(node) {
  var array = [node.item]
  node.children.forEach(function(child){
    array.push(child.item)
    child.children.forEach(function(confusedNow){
      array.push(confusedNow.item)
      aggregateNodesRecursive(confusedNow)
    })
  })
  return array
}

【问题讨论】:

  • 请添加您的尝试。
  • 闻起来像作业
  • 添加了我的尝试。不是作业哈哈

标签: javascript arrays combinations


【解决方案1】:

递归方法是:

 function traverse(node, path = [], result = []){
     if(!node.children.length)
        result.push(path.concat(node.item));
     for(const child of node.children)
         traverse(child, path.concat(node.item), result);
     return result;
 }

或者一些 hacky ES6:

const traverse = node => 
  (node.children.length?[]:[[node.item]]).concat(...node.children.map(child => 
     traverse(child).map(arr => 
         [node.item].concat(arr)
      )
  ));

现在调用traverse(node) 将返回所需的结果。

【讨论】:

  • traverse 是一个非常优雅的递归解决方案,采用现代 ECMAScript6 语法。
  • 这很棒。真的很好。
【解决方案2】:

您可以创建递归函数并首先检查当前元素是数组还是对象,然后将以前的项目编号存储在一个变量中。

var node = {"item":1,"children":[{"item":2,"children":[{"item":3,"children":[{"item":4,"children":[]},{"item":5,"children":[]},{"item":6,"children":[{"item":7,"children":[]},{"item":8,"children":[]},{"item":9,"children":[]}]}]},{"item":10,"children":[{"item":11,"children":[]},{"item":12,"children":[{"item":13,"children":[]},{"item":14,"children":[]}]}]}]}]}

const result = []
function flat(data, prev = '') {
  if (Array.isArray(data)) {
    data.forEach(e => flat(e, prev))
  } else {
    prev = prev + (prev.length ? '.' : '') + data.item;
    if (!data.children.length) result.push(prev.split('.').map(Number))
    else flat(data.children, prev)
  }
}

flat(node)
console.log(result)

【讨论】:

    【解决方案3】:

    这是使用object-scan 的解决方案。它对各种数据处理都很方便——只要你把它放在头上。

    // const objectScan = require('object-scan');
    
    const find = (tree) => objectScan(['**.children'], {
      reverse: false,
      filterFn: ({ value, parents, context }) => {
        if (value.length === 0) {
          context.push(
            parents
              .filter((p) => 'item' in p)
              .map(({ item }) => item)
              .reverse()
          );
        }
      }
    })(tree, []);
    
    const node = { item: 1, children: [{ item: 2, children: [{ item: 3, children: [{ item: 4, children: [] }, { item: 5, children: [] }, { item: 6, children: [{ item: 7, children: [] }, { item: 8, children: [] }, { item: 9, children: [] }] }] }, { item: 10, children: [{ item: 11, children: [] }, { item: 12, children: [{ item: 13, children: [] }, { item: 14, children: [] }] }] }] }] };
    
    console.log(find(node));
    /* =>
      [ [ 1, 2, 3, 4 ],
        [ 1, 2, 3, 5 ],
        [ 1, 2, 3, 6, 7 ],
        [ 1, 2, 3, 6, 8 ],
        [ 1, 2, 3, 6, 9 ],
        [ 1, 2, 10, 11 ],
        [ 1, 2, 10, 12, 13 ],
        [ 1, 2, 10, 12, 14 ] ]
    */
    .as-console-wrapper {max-height: 100% !important; top: 0}
    <script src="https://bundle.run/object-scan@13.8.0"></script>

    免责声明:我是object-scan的作者


    编辑:这是一个更有效的解决方案,以防性能很重要

    // const objectScan = require('object-scan');
    
    const find = (tree) => objectScan(['**.children'], {
      reverse: false,
      breakFn: ({ isMatch, parent, context: { stack } }) => {
        if (isMatch) {
          stack.push(parent.item);
        }
      },
      filterFn: ({ value: { length }, context: { result, stack } }) => {
        if (length === 0) {
          result.push([...stack]);
        }
        stack.pop();
      }
    })(tree, { stack: [], result: [] }).result;
    
    const node = { item: 1, children: [{ item: 2, children: [{ item: 3, children: [{ item: 4, children: [] }, { item: 5, children: [] }, { item: 6, children: [{ item: 7, children: [] }, { item: 8, children: [] }, { item: 9, children: [] }] }] }, { item: 10, children: [{ item: 11, children: [] }, { item: 12, children: [{ item: 13, children: [] }, { item: 14, children: [] }] }] }] }] };
    
    console.log(find(node));
    /* =>
      [ [ 1, 2, 3, 4 ],
        [ 1, 2, 3, 5 ],
        [ 1, 2, 3, 6, 7 ],
        [ 1, 2, 3, 6, 8 ],
        [ 1, 2, 3, 6, 9 ],
        [ 1, 2, 10, 11 ],
        [ 1, 2, 10, 12, 13 ],
        [ 1, 2, 10, 12, 14 ] ]
    */
    .as-console-wrapper {max-height: 100% !important; top: 0}
    <script src="https://bundle.run/object-scan@13.8.0"></script>

    免责声明:我是object-scan的作者

    【讨论】:

      【解决方案4】:

      您可以使用查找子节点或返回最后一个节点的路径的函数采用迭代和递归方法。

      function flatNodes(node) {
          const iter = temp => ({ item, children = [] }) => children.length
                  ? children.forEach(iter(temp.concat(item)))
                  : nodes.push(temp.concat(item));
      
          var nodes = [];
          [node].forEach(iter([]));
          return nodes;
      }
      
      var node = { item: 1, children: [{ item: 2, children: [{ item: 3, children: [{ item: 4, children: [] }, { item: 5, children: [] }, { item: 6, children: [{ item: 7, children: [] }, { item: 8, children: [] }, { item: 9, children: [] }] }] }, { item: 10, children: [{ item: 11, children: [] }, { item: 12, children: [{ item: 13, children: [] }, { item: 14, children: [] }] }] }] }] };
      
      console.log(flatNodes(node).map(a => JSON.stringify(a)));
      .as-console-wrapper { max-height: 100% !important; top: 0; }

      【讨论】:

        猜你喜欢
        • 2013-08-31
        • 2019-04-04
        • 2013-03-27
        • 1970-01-01
        • 2011-11-03
        • 1970-01-01
        • 1970-01-01
        • 2015-03-04
        • 2020-04-10
        相关资源
        最近更新 更多