将扁平结构转换为分层结构

14
我需要创建一个函数,能够将扁平对象转换为递归对象。以下是我的示例: 我有一个扁平数组:
var flatArray = [
    {
        Description: "G",
        guid: "c8e63b35",
        parent: null,
    },
    {
        Description: "Z",
        guid: "b1113b35",
        parent: "c8e63b35",
    },
    {
        Description: "F",
        guid: "d2cc2233",
        parent: "b1113b35",
    },
    {
        Description: "L",
        guid: "a24a3b1a",
        parent: null,
    },
    {
        Description: "K",
        guid: "cd3b11caa",
        parent: "a24a3b1a",
    },      
]

结果应该是:

recursiveArray = [
    {
        Description: "G",
        guid: "c8e63b35",
        parent: null,
        Children: [
            {
                Description: "Z",
                guid: "b1113b35",
                parent: "c8e63b35",
                Children: [
                    {
                        Description: "F",
                        guid: "d2cc2233",
                        parent: "b1113b35",
                    }
                ]
            }, 
        ]
    },
    {
        Description: "L",
        guid: "a24a3b1a",
        parent: null,
        Children: [
        {
            Description: "K",
            guid: "cd3b11caa",
            parent: "a24a3b1a",
        }
    }
]

请帮助我找到正确的方法。最好能提供可行的算法,因为我在理解如何正确操作时遇到了问题。我需要在递归结构中找到已检查元素的特定位置,并将其推入已找到的元素子数组中。我认为这很愚蠢和低效。有没有更快更高效的方法?

编辑:递归数组格式错误,现在已更正。我的数组没有任何排序。


你能做这样的事情吗: var recoursiveArray = []; recoursiveArray.push(flatArray[0]); recoursiveArray[0].children = []; recoursiveArray[0].children.push(flatArray[1]); - Dima Gimburg
1
'L' 对象的父级不应该是 null,而应该是 'c8e63b35' 吗? - Oka
你的数组是否以某种方式排序? - franciscod
@DimaGimburg 我可以这样做,但我需要自动完成,需要转换的结构非常庞大。 - Adam Mrozek
这里有一个类似的讨论:https://dev59.com/UmUp5IYBdhLWcg3wS2LP - Dickens A S
显示剩余2条评论
6个回答

22

这个很好用,而且易于阅读:

function flatToHierarchy (flat) {

    var roots = [] // things without parent

    // make them accessible by guid on this map
    var all = {}

    flat.forEach(function(item) {
      all[item.guid] = item
    })

    // connect childrens to its parent, and split roots apart
    Object.keys(all).forEach(function (guid) {
        var item = all[guid]
        if (item.parent === null) {
            roots.push(item)
        } else if (item.parent in all) {
            var p = all[item.parent]
            if (!('Children' in p)) {
                p.Children = []
            }
            p.Children.push(item)
        }
    })

    // done!
    return roots
}

1
是的,这很简单而且功能齐全。谢谢! - Adam Mrozek
这段代码能否找到子元素的子元素?我认为需要使用递归函数。 - Gaslan
1
@Gurkanat 是的,它适用于4级结构。在接受此答案之前,我已经检查过了。 - Adam Mrozek
可以的!试试看吧!这是因为双重处理方法:首先将所有内容放在“all”对象上,然后再链接起来! - franciscod

3
这是我的做法:

var flatArray = [{
    Description: "G",
    guid: "c8e63b35",
    parent: null,
}, {
    Description: "Z",
    guid: "b1113b35",
    parent: "c8e63b35",
}, {
    Description: "F",
    guid: "d2cc2233",
    parent: "b1113b35",
}, {
    Description: "L",
    guid: "a24a3b1a",
    parent: null,
}, {
    Description: "K",
    guid: "cd3b11caa",
    parent: "a24a3b1a",
}];

var recursiveArray = unflatten(flatArray);

alert(JSON.stringify(recursiveArray, null, 4));
<script>
function unflatten(items) {
    return items.reduce(insert, {
        res: [],
        map: {}
    }).res;
}

function insert(obj, item) {
    var parent     = item.parent;
    var map        = obj.map;
    map[item.guid] = item;

    if (parent === null) obj.res.push(item);
    else {
        var parentItem = map[parent];

        if (parentItem.hasOwnProperty("Children"))
            parentItem.Children.push(item);
        else parentItem.Children = [item];
    }

    return obj;
}
</script>

当然,这仅在您的flatArray具有每个父级出现在其子级之前的属性时才有效。希望对您有所帮助。

1
这是非常好的解决方案,也许我可以从你的答案中学到一些新东西。谢谢! :) - Adam Mrozek

0

这个递归函数可能对你有好处:

var flatArray = [{
  Description: "G",
  guid: "c8e63b35",
  parent: null,
  Children: []
}, {
  Description: "Z",
  guid: "b1113b35",
  parent: "c8e63b35",
  Children: []
}, {
  Description: "F",
  guid: "d2cc2233",
  parent: "b1113b35",
  Children: []
}, {
  Description: "L",
  guid: "a24a3b1a",
  parent: null,
  Children: []
}, {
  Description: "K",
  guid: "cd3b11caa",
  parent: "a24a3b1a",
  Children: []
}, ];




for (var i = 0; i < flatArray.length; i++) {
  recursive(flatArray[i]);
}


function recursive(a) {
  for (var i = 0; i < flatArray.length; i++) {
    if (flatArray[i].parent == a.guid) {
      var b = flatArray[i];
      recursive(b);
      a.Children.push(b);
    }
  }
}


console.log(flatArray)


0

var flatArray = [
    {
        Description: "G",
        guid: "c8e63b35",
        parent: null,
    },
    {
        Description: "Z",
        guid: "b1113b35",
        parent: "c8e63b35",
    },
    {
        Description: "F",
        guid: "d2cc2233",
        parent: "b1113b35",
    },
    {
        Description: "L",
        guid: "a24a3b1a",
        parent: null,
    },
    {
        Description: "K",
        guid: "cd3b11caa",
        parent: "a24a3b1a",
    },      
];

//for printing
function htmlPrint(obj) {
  document.write('<pre>'+JSON.stringify(obj,null,2)+'</pre>');
};

var guids = {};
var roots = [];

flatArray.forEach(function(node){ 
  guids[node.guid] = node;       //save into a hash
  node.Children = [];            //make sure it has a children array
  //save it as root if it is a root
  if(node.parent === null){ roots.push(node);}
});
flatArray.forEach(function(node){ 
  //if it has a parent, add self to parent's children
  var parent = guids[node.parent]; 
  if(parent)  parent.Children.push(node); 
});
htmlPrint(roots);


0

我尝试用伪代码编写算法,最终得到了几乎能够工作的 JS 代码(可能需要一些额外的验证/检查),但是它展示了解决问题的一般方法。

//Lets separate children (nodes with a parent) from roots (nodes without a parent)
var children = flatArray.filter(function(object){
    return object.parent !== null;
});

var roots = flatArray.filter(function(object){
    return object.parent === null;
});

//And add each child to the nodes tree
children.foreach(function(child){
    recursiveAdd(roots, child);
});

//To add a children node, node tree is searched recursively for a parent
function recursiveAdd(nodes, child){
    nodes.foreach(function(parent){
        if(parent.guid === child.parent){
            parent.Children = parent.Children | [];
            parent.Children.add(child);
        } else if(parent.Children) {
            recursiveAdd(parent.Children, child);
        }
    });
}

//Temporary children array can be garbage collected
children = null;
//Resulting node tree
var recursiveArray = roots;

0

你可以在 Angular 中使用下面的代码。

flatToHierarchy(flat: any[], parent: any = null, Key: string = 'id', parentKey: string = 'parentId') {
  var leafs: any = [];

  if (!parent) {
    leafs = flat.filter((x: { [x: string]: any; }) => x[parentKey] === null);
  } else {
    leafs = flat.filter((x: { [x: string]: any; }) => x[parentKey] === parent[Key]);
  }

  if (!leafs || leafs.length == 0) {
    return;
  } else {
      leafs.forEach((item: { children: any[]; }) => {
      item.children = [];
      item.children = this.flatToHierarchy(flat, item);
    });
  }
  return leafs;
}

使用类似这样的方式

 this.flatToHierarchy(flatItems);

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