Javascript 从对象树构造平面数组

Javascript 从对象树构造平面数组,javascript,arrays,algorithm,tree,hierarchy,Javascript,Arrays,Algorithm,Tree,Hierarchy,假设我有一个如下的对象树,可能是使用这里的优秀算法创建的: (特别是在本例中,该函数返回的数组作为子对象数组属性嵌套在其他空对象中。) 如何将其转换回平面阵列?此函数将完成此任务,并为每个对象添加一个级别指示器。treeObj的直接子级将为1级,其子级将为2级,等等。父级属性也将更新 function flatten(treeObj, idAttr, parentAttr, childrenAttr, levelAttr) { if (!idAttr) idAttr = 'id';

假设我有一个如下的对象树,可能是使用这里的优秀算法创建的:

(特别是在本例中,该函数返回的数组作为
子对象
数组属性嵌套在其他空对象中。)


如何将其转换回平面阵列?

此函数将完成此任务,并为每个对象添加一个级别指示器。treeObj的直接子级将为1级,其子级将为2级,等等。
父级
属性也将更新

function flatten(treeObj, idAttr, parentAttr, childrenAttr, levelAttr) {
    if (!idAttr) idAttr = 'id';
    if (!parentAttr) parentAttr = 'parent';
    if (!childrenAttr) childrenAttr = 'children';
    if (!levelAttr) levelAttr = 'level';

    function flattenChild(childObj, parentId, level) {
        var array = []; 

        var childCopy = angular.extend({}, childObj);
        childCopy[levelAttr] = level;
        childCopy[parentAttr] = parentId;
        delete childCopy[childrenAttr];
        array.push(childCopy);

        array = array.concat(processChildren(childObj, level));

        return array;
    };

    function processChildren(obj, level) {
        if (!level) level = 0;
        var array = [];

        obj[childrenAttr].forEach(function(childObj) {
            array = array.concat(flattenChild(childObj, obj[idAttr], level+1));
        });

        return array;
    };

    var result = processChildren(treeObj);
    return result;
};
此解决方案利用Angular的
Angular.extend()
函数执行子对象的复制。将其与任何其他库的等效方法或本机函数连接起来应该是一个微不足道的更改

上述示例的输出为:

[{
    "id": 1,
    "title": "home",
    "parent": null,
    "level": 1
}, {
    "id": 2,
    "title": "about",
    "parent": null,
    "level": 1
}, {
    "id": 3,
    "title": "team",
    "parent": 2,
    "level": 2
}, {
    "id": 4,
    "title": "company",
    "parent": 2,
    "level": 2
}]
还值得注意的是,此函数不保证数组按
id
排序;它将基于操作期间遇到单个对象的顺序


下面是我的贡献:

function flatNestedList(nestedList, childrenName, parentPropertyName, idName, newFlatList, parentId) {

        if (newFlatList.length === 0)
            newFlatList = [];

        $.each(nestedList, function (i, item) {
            item[parentPropertyName] = parentId;
            newFlatList.push(item);
            if (item[childrenName] && item[childrenName].length > 0) {
                //each level
                flatNestedList(item[childrenName], childrenName, parentPropertyName, idName, newFlatList, item[idName]);
            }
        });

        for (var i in newFlatList)
            delete (newFlatList[i][childrenName]);
    }

希望您熟悉es6:

let flatten = (children, extractChildren) => Array.prototype.concat.apply(
  children, 
  children.map(x => flatten(extractChildren(x) || [], extractChildren))
);

let extractChildren = x => x.children;

let flat = flatten(extractChildren(treeStructure), extractChildren)
               .map(x => delete x.children && x);
UPD:

抱歉,尚未注意到您需要设置父级和级别。请在下面找到新功能:

let flatten = (children, getChildren, level, parent) => Array.prototype.concat.apply(
  children.map(x => ({ ...x, level: level || 1, parent: parent || null })), 
  children.map(x => flatten(getChildren(x) || [], getChildren, (level || 1) + 1, x.id))
);

仅在假设每个项目都有子属性的情况下,尝试执行此操作

class TreeStructureHelper {
   public toArray(nodes: any[], arr: any[]) {
    if (!nodes) {
      return [];
    }
    if (!arr) {
      arr = [];
    }
    for (var i = 0; i < nodes.length; i++) {
      arr.push(nodes[i]);
      this.toArray(nodes[i].children, arr);
    }
  return arr;
 }
}
这是数据:

 const data = {
      id: '1',
      children: [
        {
          id: '2',
          children: [
            {
              id: '4',
              children: [
                {
                  id: '5'
                },
                {
                  id: '6'
                }
              ]
            },
            {
              id: '7'
            }
          ]
        },
        {
          id: '3',
          children: [
            {
              id: '8'
            },
            {
              id: '9'
            }
          ]
        }
      ]
    }
在React.JS中,只需在state中声明一个数组字段,并将项推送到该数组中

  const getAllItemsPerChildren = item => {
    array.push(item);
    if (item.children) {
      return item.children.map(i => getAllItemsPerChildren(i));
    }
  }
函数调用后,处于状态的数组将保存以下所有项:


我发现了很多问题,询问如何从数组创建树,但没有一个问题是从另一个方向转换回来的,因此我发布了我提出的解决方案。您应该调用函数
flattchildrenof
或类似的函数,因为它始终忽略根。没有人说根不能是重要的节点。请记住,上面的答案是递归的,因此可以改进。因为我找不到实现O(n)解决方案的npm模块,所以我创建了以下一个(经过单元测试,100%的代码覆盖率,只有0.5 kb大小,并且包括打字。也许这对某些人有帮助:npmjs.com/package/performant-array-to-treeWow,我不敢相信这是多么少的代码,它工作得非常完美。ES6很棒!哦,级别指示器也没有必要,我只是为了说明目的将它添加到我的实现中。是的,ES6和接下来是鼓舞人心的!很高兴读到这篇文章!此外,如果我们用匿名函数替换lambdas,到目前为止,第一个解决方案将是有效的es5。
 const data = {
      id: '1',
      children: [
        {
          id: '2',
          children: [
            {
              id: '4',
              children: [
                {
                  id: '5'
                },
                {
                  id: '6'
                }
              ]
            },
            {
              id: '7'
            }
          ]
        },
        {
          id: '3',
          children: [
            {
              id: '8'
            },
            {
              id: '9'
            }
          ]
        }
      ]
    }
  const getAllItemsPerChildren = item => {
    array.push(item);
    if (item.children) {
      return item.children.map(i => getAllItemsPerChildren(i));
    }
  }