如何将数组转换为分层数组

时间:2017-09-15 13:51:14

标签: javascript arrays

我有一些数据

var currentData = [
    {'ticket':'CAP', 'child':'CT-1'},
    {'ticket':'CAP', 'child':'CT-2'},
    {'ticket':'CT-1', 'child':'CT-1-A'},
    {'ticket':'CT-1', 'child':'CT-1-B'}
];

数据是平的,我需要将其转换为:

{
    'ticket': 'CAP',
    children : [{
        'ticket' : 'CT-1',
        'children' : [{
            'ticket' : 'CT-1-A',
            'children' : []
        }, {
            'ticket' : 'CT-1-B',
            'children' : []
        }],
        [{
            'ticket' : 'CT-2',
            'children' : []
        }]
    }]
}

(我认为上述内容有效)?

我很失落。我将展示我的努力,但是,我不确定我的方法是否正确。



var currentData = [{'ticket':'cap', 'child':'CT-1'},{'ticket':'cap', 'child':'CT-2'}, {'ticket':'CT-1', 'child':'CT-1-A'},{'ticket':'CT-1', 'child':'CT-1-B'}];

var newList = [];
function convert(list){
    if (newList.length <= 0){
        var child = [];
        var emptyChild = [];
        child.push({'ticket': list[0].child, 'child': emptyChild });
        newList.push({'ticket': list[0].ticket, 'children' : child});
        list.splice(0,1);
    } // the if statement above works fine
    
    for(var i = 0;  i < list.length; i++) {
        var ticket = list[i].ticket;
        for(var j = 0; j < newList.length; j++) {
            if (newList[j].ticket == ticket){
                var child;
                var emptyChild = [];
                child = {'ticket': list[i].child, 'child': emptyChild };
                newList[j].children.push(child);
                list.splice(i,1);
                break;
            } // the if above works
            else{
                var child2 = getFromChildren(ticket, newList, list[i]); // child2 is Always null, even if getFromChildren returns an object
                newList[j].children.push(child2);
                list.splice(i,1);
                break;
            }
        }
    }   
    
    if (list.length > 0){
        convert(list);
    }
}

function getFromChildren(ticket, list, itemToAdd){

    if (list == null || list[0].children == null)
        return;
    
    for(var i = 0; i < list.length; i++) {
        if (list[i] == null)
        return;
        
        if (list[i].ticket == ticket){
            list[i].child.push(itemToAdd.child); // ** can't do this, javascript passes by value, not by reference :(
        } else{
            getFromChildren(ticket, list[i].children, itemToAdd);
        }
    }
}

convert(currentData);
&#13;
&#13;
&#13;

我觉得我弄得一团糟。在评论中,我发了一个**,解释说由于JavaScript没有通过引用传递它不起作用,但是在further reading我不认为这是正确的,因为我&#39 ; m传递通过引用的对象?

修改

currentData显示的数据并不总是从根本上开始

5 个答案:

答案 0 :(得分:2)

&#13;
&#13;
function convert(arr) {
  var children = {};                                         // this object will hold a reference to all children arrays

  var res = arr.reduce(function(res, o) {                    // for each object o in the array arr
    if(!res[o.ticket]) {                                     // if there is no object for the element o.ticket
      res[o.ticket] = {ticket: o.ticket, children: []};      // then creates an object for it
      children[o.ticket] = res[o.ticket].children;           // and store a reference to its children array
    }
    if(!res[o.child]) {                                      // if there is no object for the element o.child
      res[o.child] = {ticket: o.child, children: []};        // then creates an object for it
      children[o.child] = res[o.child].children;             // and store a reference to its children array
    }
    return res;
  }, {});
  
  arr.forEach(function(o) {                                  // now for each object o in the array arr
    children[o.ticket].push(res[o.child]);                   // add the object of o.child (from res) to its children array
    delete res[o.child];                                     // and remove the child object from the object res
  });
  
  return res;
}



var currentData = [
    {'ticket':'CAP', 'child':'CT-1'},
    {'ticket':'CAP', 'child':'CT-2'},
    {'ticket':'CT-1', 'child':'CT-1-A'},
    {'ticket':'CT-1', 'child':'CT-1-B'}
];

console.log(convert(currentData));
&#13;
&#13;
&#13;

<强>解释

reduce部分为每个元素(子元素或非元素)创建一个形式为{ ticket: "...", children: [] }的对象。因此,在reduce之后,对象res将是:

res = {
    'CAP': { ticket: 'CAP', children: [] },
    'CT-1': { ticket: 'CT-1', children: [] },
    'CT-2': { ticket: 'CT-2', children: [] },
    'CT-1-A': { ticket: 'CT-1-A', children: [] },
    'CT-1-B': { ticket: 'CT-1-B', children: [] },
}

现在forEach位再次循环遍历数组,现在对于每个对象,它从上面的.child获取res的对象,将其推入{{1} } object&#39; s .ticket(对它的引用存储在children对象中),然后从对象children中删除.child对象。

答案 1 :(得分:1)

下面使用reduce来将数据分组到Map,然后我将数据转换为上面显示的对象。您需要使用现代浏览器在代码段下运行,或使用像babeljs这样的转换器将其转换为es5语法。

&#13;
&#13;
let currentData = [
    {'ticket':'CAP', 'child':'CT-1'},
    {'ticket':'CAP', 'child':'CT-2'},
    {'ticket':'CT-1', 'child':'CT-1-A'},
    {'ticket':'CT-1', 'child':'CT-1-B'}
];

let children = currentData.map(e => e.child);
currentData.sort((a,b) => children.indexOf(a.ticket));

let res = currentData.reduce((a,b) => {
    if (! children.includes(b.ticket)) {
        return a.set(b.ticket, (a.get(b.ticket) || [])
            .concat({ticket: b.child,
                children: currentData
                    .filter(el => el.ticket === b.child)
                    .map(el => ({ticket: el.child, children: []}))}))
    }
    return a;
}, new Map);

let r = {};

for (let [key,value] of res.entries()) {
    r.ticket = key;
    r.children = value;
}

console.log(r);
&#13;
&#13;
&#13;

答案 2 :(得分:1)

使用递归的解决方案,可以更改起始节点。

&#13;
&#13;
[{date: "11.04.15", name: "xxx", info: ""},
 {date: "11.04.15", name: "yyy", info: ""}]
&#13;
var currentData = [{'ticket': 'cap','child': 'CT-1'}, {'ticket': 'cap','child': 'CT-2'}, {'ticket': 'CT-1','child': 'CT-1-A'}, {'ticket': 'CT-1','child': 'CT-1-B'}];

function convert(data, start){
  return {
    ticket: start,
    childs: data.filter(d => d.ticket == start)
                .reduce((curr, next) => curr.concat([next.child]), [])
                .map(c => convert(data, c))
  }
}

let result = convert(currentData, 'cap');

console.log(result);
&#13;
&#13;
&#13;

答案 3 :(得分:1)

我会采用一种简单的for方法,如下所示:

&#13;
&#13;
var currentData = [
    {'ticket':'CAP', 'child':'CT-1'},
    {'ticket':'CAP', 'child':'CT-2'},
    {'ticket':'CT-1', 'child':'CT-1-A'},
    {'ticket':'CT-1', 'child':'CT-1-B'}
];
var leafs = {};
var roots = {};
var tickets = {};
for(var i=0; i<currentData.length; i++){
    var ticket = currentData[i].ticket;
    var child = currentData[i].child;
    if(!tickets[ticket]){
        tickets[ticket] = {ticket:ticket,children:[]};
        if(!leafs[ticket]){
            roots[ticket] = true;
        }
    }
    if(!tickets[child]){
        tickets[child] = {ticket:child,children:[]};
    }
    delete roots[child];
    leafs[child] = true;
    tickets[ticket].children.push(tickets[child]);
}
for(var ticket in roots){
    console.log(tickets[ticket]);
}
&#13;
&#13;
&#13;

答案 4 :(得分:0)

好吧,如果你不熟悉reducemapforEach有回调的回调,那么这是我带来的方法,代码是平的,存储对象引用另一个地图对象,完全迭代源数组。

代码更清晰,如果可以理解的话,我会解释添加评论;

&#13;
&#13;
var currentData = [
    {'ticket':'CT-1', 'child':'CT-1-A'},
    {'ticket':'CT-1', 'child':'CT-1-B'},
    {'ticket':'CAP', 'child':'CT-1'},
    {'ticket':'CAP', 'child':'CT-2'}
];
function buildHierarchy(flatArr) {
    let root = {},
        nonRoot = {},
        tempMap = {};
    Object.setPrototypeOf(root, nonRoot);
    for (let idx = 0; idx < flatArr.length; idx++) {
        let currTicket = flatArr[idx];
        let tempTicket = tempMap[currTicket.ticket] || {ticket: currTicket.ticket, children: []};
        tempMap[currTicket.ticket] = tempTicket;
        if (currTicket.child) {
            let tempChild = tempMap[currTicket.child] || {ticket: currTicket.child, children: []};
            tempTicket.children.push(tempChild);
            tempMap[currTicket.child] = tempChild;
            delete root[tempChild.ticket];
            nonRoot[tempChild.ticket] = true;
        }
        root[tempTicket.ticket] = true;

    }
    return tempMap[Object.keys(root)[0]];
}

console.log(buildHierarchy(currentData));
&#13;
&#13;
&#13;

我已经更改了源数组的顺序,以便将根对象放在任何位置,代码应该在那里工作。

相关问题