从具有父ID的扁平结构转换为分层的JSON

15

http://jsfiddle.net/eYgGK/

我从另一篇文章中盗用了这个脚本:

function convertToHierarchy() {
  var arry = [
    { "Id": "1", "Name": "abc", "Parent": "", "attr": "abc" },
    { "Id": "2", "Name": "abc", "Parent": "1", "attr": "abc" },
    { "Id": "3", "Name": "abc", "Parent": "2", "attr": "abc" },
    { "Id": "4", "Name": "abc", "Parent": "2", "attr": "abc" }
  ];
  var nodeObjects = createStructure(arry);
  for (var i = nodeObjects.length - 1; i >= 0; i--) {
    var currentNode = nodeObjects[i];
    if (currentNode.value.Parent === "") {
      continue;
    }
    var parent = getParent(currentNode, nodeObjects);

    if (parent === null) {
      continue;
    }

    parent.children.push(currentNode);
    nodeObjects.splice(i, 1);
  }
  console.dir(nodeObjects);
  return nodeObjects;
}

function createStructure(nodes) {
  var objects = [];

  for (var i = 0; i < nodes.length; i++) {
    objects.push({
      value: nodes[i],
      children: []
    });
  }

  return objects;

}

function getParent(child, nodes) {
  var parent = null;

  for (var i = 0; i < nodes.length; i++) {
    if (nodes[i].value.Id === child.value.Parent) {
      return nodes[i];
    }
  }

  return parent;
}

这个脚本会产生:

[{
    "value": {
        "Id": "1",
        "Name": "abc",
        "Parent": "",
        "attr": "abc"
    },
    "children": [{
        "value": {
            "Id": "2",
            "Name": "abc",
            "Parent": "1",
            "attr": "abc"
        },
        "children": [{
            "value": {
                "Id": "4",
                "Name": "abc",
                "Parent": "2",
                "attr": "abc"
            },
            "children": []
        }, {
            "value": {
                "Id": "3",
                "Name": "abc",
                "Parent": "2",
                "attr": "abc"
            },
            "children": []
        }]
    }]
}]

我要找的是:

[{
    "Id": "1",
    "Name": "abc",
    "Parent": "",
    "attr": "abc",
    "children": [{

        "Id": "2",
        "Name": "abc",
        "Parent": "1",
        "attr": "abc",
        "children": [{

            "Id": "4",
            "Name": "abc",
            "Parent": "2",
            "attr": "abc"
        }, {

            "Id": "3",
            "Name": "abc",
            "Parent": "2",
            "attr": "abc"
           
        }]
    }]
}]

我需要首先摆脱“value”包装,其次是空的子节点。我知道我可以编写一个清理脚本,但那不是最佳实践。 如果有人知道如何修复,或建议使用不同的脚本,那将是很棒的!

5个回答

31

试试这样做

var arry = [{ "Id": "1", "Name": "abc", "Parent": "", "attr": "abc" },
               { "Id": "2", "Name": "abc", "Parent": "1", "attr": "abc" },
               { "Id": "3", "Name": "abc", "Parent": "2", "attr": "abc" },
               { "Id": "4", "Name": "abc", "Parent": "2", "attr": "abc" }];

function convert(array){
    var map = {};
    for(var i = 0; i < array.length; i++){
        var obj = array[i];
        obj.items= [];

        map[obj.Id] = obj;

        var parent = obj.Parent || '-';
        if(!map[parent]){
            map[parent] = {
                items: []
            };
        }
        map[parent].items.push(obj);
    }

    return map['-'].items;

}

var r = convert(arry)

演示:Fiddle

结果

[{
    "Id" : "1",
    "Name" : "abc",
    "Parent" : "",
    "attr" : "abc",
    "children" : [{
                "Id" : "2",
                "Name" : "abc",
                "Parent" : "1",
                "attr" : "abc",
                "children" : [{
                            "Id" : "3",
                            "Name" : "abc",
                            "Parent" : "2",
                            "attr" : "abc",
                            "children" : []
                        }, {
                            "Id" : "4",
                            "Name" : "abc",
                            "Parent" : "2",
                            "attr" : "abc",
                            "children" : []
                        }]
            }]
}]

阿伦,你知道我怎么把“children”属性的标题改成“items”吗? - I Stand With Russia
1
如果我改变数组元素的顺序,它将无法工作。例如,如果我将数组元素(即ID 1)的位置更改为数组的最后一个,则此方法将无法工作。http://jsfiddle.net/MkTA6/146/。是否有任何方法也可以容纳这个问题? - Jetson John
@JetsonJohn 你弄清楚了吗,如果数据的id不是连续的,我有一个类似的需求。id不是连续的,所以所有的子项都是空的。 - user525146
@ArunPJohny,我遇到了类似的问题,JSON对象还有一层嵌套。我在这里发布了一个相关的问题 - https://stackoverflow.com/questions/49204904/hierarchical-json-based-on-conversation-between-two-entities-determined-by-paren。你能否提供解决方法? - user2703788

8

@Arun P Johny的答案很好,但它有一个问题:当数组未排序时,它会刷新子树。我将其更新为以下内容。

var arry = [
                { "Id": "5", "Name": "abc", "Parent": "3", "attr": "abc" },
               { "Id": "2", "Name": "abc", "Parent": "1", "attr": "abc" },
               { "Id": "4", "Name": "abc", "Parent": "2", "attr": "abc" },
               { "Id": "3", "Name": "abc", "Parent": "2", "attr": "abc" },
               { "Id": "1", "Name": "abc", "Parent": "", "attr": "abc" }
               ];

function convert(array){
    var map = {}
    for(var i = 0; i < array.length; i++){
        var obj = array[i]
        if(!(obj.Id in map)){
            map[obj.Id] = obj
            map[obj.Id].children = []
        }

        if(typeof map[obj.Id].Name == 'undefined'){
            map[obj.Id].Id = obj.Id
            map[obj.Id].Name = obj.Name
            map[obj.Id].attr = obj.attr
            map[obj.Id].Parent= obj.Parent
        }

        var parent = obj.Parent || '-';
        if(!(parent in map)){
            map[parent] = {}
            map[parent].children = []
        }

        map[parent].children.push(map[obj.Id])
    }
    return map['-']
}
console.log(JSON.stringify(convert(arry)))

结果如下:
{
  "children": [
    {
      "children": [
        {
          "Id": "2",
          "Name": "abc",
          "Parent": "1",
          "attr": "abc",
          "children": [
            {
              "Id": "4",
              "Name": "abc",
              "Parent": "2",
              "attr": "abc",
              "children": []
            },
            {
              "children": [
                {
                  "Id": "5",
                  "Name": "abc",
                  "Parent": "3",
                  "attr": "abc",
                  "children": []
                }
              ],
              "Id": "3",
              "Name": "abc",
              "attr": "abc",
              "Parent": "2"
            }
          ]
        }
      ],
      "Id": "1",
      "Name": "abc",
      "attr": "abc",
      "Parent": ""
    }
  ]
}

4
function convert(rows) {
    function exists(rows, Parent) {
            for (var i = 0; i < rows.length; i++) {
                    if (rows[i].Id == Parent) return true;
            }
            return false;
    }
    var nodes = [];
    // get the top level nodes
    for (var i = 0; i < rows.length; i++) {
            var row = rows[i];
            if (!exists(rows, row.Parent)) {
                    nodes.push({
                            id: row.Id,
                            name: row.Name,
                            attr: row.attr
                    });
            }
    }
    var toDo = [];
    for (var i = 0; i < nodes.length; i++) {
            toDo.push(nodes[i]);
    }
    while (toDo.length) {
            var node = toDo.shift();
            // the parent node
            // get the children nodes
            for (var i = 0; i < rows.length; i++) {
                    var row = rows[i];
                    if (row.Parent == node.Id) {
                            var child = {
                                    Id: row.Id,
                                    Name: row.Name,
                                    attr: row.attr
                            };
                            if (node.options) {
                                    node.options.push(child);
                            } else {
                                    node.options = [child];
                            }
                            toDo.push(child);
                    }
            }
    }
    return nodes;}

1
我认为这个答案更好。 即使顶层节点不是数组的第一个元素,这也可以正常工作。 - Yasuyuki Uno

2

改进版本,接受参数。

function convert(rows, options) {
    var options = options || {};
    var idProp = options.id || 'id';
    var parentProp = options.parent || 'parent';
    var childProp = options.children || 'children';

    function exists(rows, parent) {
        for (var i = 0; i < rows.length; i++) {
            if (rows[i][idProp] == parent) return true;
        }
        return false;
    }

    var nodes = [];

    // get the top level nodes
    for (var i = 0; i < rows.length; i++) {
        var row = rows[i];
        if (!exists(rows, row[parentProp])) {
            nodes.push(Object.assign({}, row));
        }
    }

    var toDo = [];
    for (var i = 0; i < nodes.length; i++) {
        toDo.push(nodes[i]);
    }

    while (toDo.length) {
        var node = toDo.shift();

        // the parent node
        // get the children nodes
        for (var i = 0; i < rows.length; i++) {
            var row = rows[i];
            if (row[parentProp] == node[idProp]) {
                var child = Object.assign({}, row);
                node[childProp] = node[childProp] || [];
                node[childProp].push(child);
                toDo.push(child);
            }
        }
    }

    return nodes;
}

// Usage
var data = [...];
convert(data);
// Or
convert(data, { id: 'Id', parent: 'Parent', children: 'Children' });


0

// 尝试像这样...................

function prepareFlatArrayListToTreeViewList(arry) {
    var treeviewList= angular.copy(flatListToTreeViewData(arry));
    return treeviewList;
}

// 返回 JSON 格式数据

function flatListToTreeViewData(dataList) {
    var tree = [],
        mappedArr = {},
        arrElem,
        mappedElem;

    // First map the nodes of the array to an object -> create a hash table.
    for (var i = 0, len = dataList.length; i < len; i++) {
        arrElem = dataList[i];
        mappedArr[arrElem.id] = arrElem;
        mappedArr[arrElem.id]['children'] = [];
    }

    for (var id in mappedArr) {
        if (mappedArr.hasOwnProperty(id)) {
            mappedElem = mappedArr[id];

            // If the element is not at the root level, add it to its parent array of children.
            if (mappedElem.parentID) {
                mappedArr[mappedElem['parentID']]['children'].push(mappedElem);
            }
                // If the element is at the root level, add it to first level elements array.
            else {
                tree.push(mappedElem);
            }
        }
    }
    return tree;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接