【问题标题】:Building an object graph from flat object array in javascript在 javascript 中从平面对象数组构建对象图
【发布时间】:2011-09-13 21:25:32
【问题描述】:

我有一个 javascript 对象数组,其中的对象看起来像这样:

项目ID
名称
parentItemId

我想构建一个图表,其中父项包含子项数组,如果适用,这些子项具有子项数组。

有什么好的方法来解决这个问题?

【问题讨论】:

  • 你可以试试jqplot.com这样的插件
  • 嗨约瑟夫,有趣的项目!但我不是指图表/图表.. 只是一个嵌套对象“图表”..

标签: javascript arrays object-graph


【解决方案1】:
function objectGraph(items)
{
    var items_by_id = {};
    var roots = [];
    var i;

    // Build an id->object mapping, so we don't have to go hunting for parents
    for (i = 0; i < items.length; ++i) {
        items_by_id[items[i].itemId] = items[i];
        items[i].children = [];
    }

    for (i = 0; i < items.length; ++i) {
        var parentId = items[i].parentItemId;
        // If parentId is null, this is a root; otherwise, it's parentId's kid
        var nodes = (parentId === null) ? roots : items_by_id[parentId].children;
        nodes.push(items[i]);
    }
    return roots;
}

注意,这段代码给每个节点一个children 属性,如果一个节点没有孩子,这个属性是空的。我个人觉得它比每个节点可能-或-可能-没有孩子更简单、更一致;你可以遍历children 而不必担心它是否存在。叶节点将具有children.length == 0

如果你能保证你只有一个根,你可以return roots[0];而不是返回数组。

【讨论】:

  • 所以不是做 if(children) 你做 if(children.length == 0)...hmm 我想如果你想全面迭代项目然后决定它是否有用一片叶子与否。我通常会反其道而行之。
  • 你可以使用if (children),而不是if (children)。无论哪种方式都可以正常工作(检查列表是否存在以及它是否包含任何内容),但后者意味着更少的特殊情况,并且不必担心是否会出现未定义对象错误。如果需要,您可以像对待任何其他叶节点一样对待叶节点,并迭代其子节点;你只是不会有任何孩子可以做任何事情。 :) 请注意,DOM 的工作方式类似;每个节点都有一个子节点列表,即使该列表为空。
  • 确实如此,firefox(可能所有浏览器)也为空 DOM 元素的 children.length 返回 0。我的解决方案也做同样的事情哈哈。
  • +1 这个解决方案是可行的,感谢 cHao 的课程。我现在要对自己的一些代码进行一些更改:)
  • 我将在此提供我的道具。它只循环整个数组两次,因为我们提出的递归选项将在每次递归调用时循环整个数组。聪明的工作!我建议也许将数组的.length 属性缓存在一个变量中,因为在某些浏览器中它可以加速循环。
【解决方案2】:

当您构建“树构建器功能”时,您必须确定“顶级事物”是单个项目还是项目数组。既然你说 itemS 我们就用一个数组。不同的是你传入的参数并返回,如果它是一个数组我们传递parentId,否则我们传递id。

function buildTree(parentId, list) {
  var nodes = [];
  for (var i=0, l; l = list[i]; i++) {
    if (l.parentId === parentId) {
// if you need "myList" intact afterwards remove the next line at the cost of efficiency
      list.splice(i, 1); i--;  
      nodes.push({
        id: l.id
        ,parentId: l.parentId
        ,name: l.name
        ,children: buildTree(l.id, list)
      });
    }
  }
  return nodes;
}

var myTree = buildTree(null, myList);

【讨论】:

  • 我对递归解决方案(或者更确切地说,我见过和考虑过的递归解决方案)的唯一主要抱怨是,对于大量节点来说,它会非常慢。每个非根节点都需要扫描整个数组以查找其子节点,从而使算法 O(n^2) 或更糟,除非数组推送是恒定时间的。它在概念上更简单,但如果需要一天的时间运行,这并不重要。 :)
  • 已修复 - 删除找到的元素 - 更好,但仍然不是超快。
  • 不幸的是,AFAIK 拼接数组是一个 O(n) 操作。对于非常大的数组,这实际上可能会减慢速度,因为您现在可能处于 O(n^3)。
【解决方案3】:

这有点粗略,但如果我正确地收集了您的问题,它应该可以完成工作。它应该返回一个顶级对象的数组,这些对象的子对象在它们下面正确组织。

请注意,这将适用于 N 级子对象,而不仅仅是单个级别。

var finalArray = [];
var YOUR_RAW_ARRAY = [];

var buildObjectGraph = function(inputArray){
    var i = 0, len = inputArray.length;
    var returnVal = [];

    for(;i<len;i++){
        if(inputArray[i].parentItemId === null){
            findChildren(inputArray[i], inputArray);
            returnVal.push(inputArray[i]);
        }
    }

    var findChildren = function(root){
        var i = 0, i2 = 0, len = rawDataArray.length, len2 = 0;

        for(;i<len;i++){
            if(inputArray[i].parentItemId === root.itemId){
                if(root.children){
                    root.children.push(inputArray[i]);
                }else{
                    root.children = [];
                    root.children.push(inputArray[i]);
                }
            }
        }

        //now call it recursively
        len2 = root.children.length;
        if(len2 > 0){
            for(;i2 < len2; i2++){
                findChildren(root.children[i2]);
            }
        }
    };
    return returnVal;
};

//Then execute it
finalArray = buildObjectGraph(YOUR_RAW_ARRAY); 

【讨论】:

  • 嘿,非常感谢!有没有办法通过删除 RAW 数组中已经推送的项目来优化?
  • 我真的考虑过在处理过程中尝试 splice 取出输入数组的片段,但是由于这两个函数都使用指向 inputArray 的指针,这真的会弄乱迭代器试图循环遍历每个项目。此外,查看拼接不同数组以传递给findChildren 函数的性能实际上可能不会更好,因为这可能会产生一些开销。最后,检查数组的.length 属性确实会减慢速度(尤其是在大型数组上)。最好先将它缓存在一个变量中,然后再循环。
猜你喜欢
  • 1970-01-01
  • 2018-05-09
  • 1970-01-01
  • 2015-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多