【问题标题】:Generating nested list from flat list with parent/child lists in JavaScript在 JavaScript 中从带有父/子列表的平面列表生成嵌套列表
【发布时间】:2019-03-11 16:46:04
【问题描述】:

我正在构建一个网络应用程序,它需要处理嵌套的地理数据以显示在树形视图中,但也可以搜索。原始数据如下所示:

id:1, name:UK
id:2: name: South-East, parentId: 1
id:3: name: South-West, parentId:1
id:4: name: Berkshire, parentId: 2
id:5: name: Reading, parentId: 4

我希望它看起来像这样:

id:1: name UK, children[ 
 {id: 2, name: South-East, children:[
    {id:4: name: Berkshire, children: [
       {id:5: name: Reading}
     ]
  }, 
   {id:3: name: South-West}
]

这样每个地理位置都有一个“children”数组属性,其中包含所有子区域,每个子区域都有另一个“children”数组属性,以此类推。拥有“父”属性可能也很有意义,因此我可以从任何子项导航到其父项。

我还需要能够搜索列表 - 搜索树的每个分支可能需要一些时间,所以也许我还需要将列表保持为平面格式。

我知道我如何可以在 JavaScript 中做到这一点(可能使用 jLinq 进行过滤、分组和排序),但我不知道它会有多快。有没有人已经在 J​​avaScript 中尝试过,或者知道解决这个问题的任何通用算法/模式?

【问题讨论】:

  • 想通了...延迟加载。我们不需要一次显示所有数据,只需要按需显示(这是一个很大的数据结构,人们会点击进入他们需要的位)。查找相关项目并在需要时将它们添加到“children”属性会更容易,而不是提前做很多事情......
  • 您介意将您的解决方案作为答案发布在下面,以便我们将其从未回答列表中删除吗?谢谢。

标签: javascript algorithm


【解决方案1】:

实际上将平面数组制作成树并很快完成并不难,我认为最慢的一点是将数据结构的定义放到页面上(因此你为什么延迟加载是成功的! )。这可以通过缩小数据结构定义来帮助解决。

在 Javascript 中我是这样做的:

    //Make the data definition as small as possible..
    //each entry is [ name, parent_pos_in_array]..
    //note: requires that a parent node appears before a child node..
    var data = [
        ["UK", -1], //root..
        ["South-East", 0],
        ["South-West", 0],
        ["Berkshire", 1],
        ["Reading", 3]
        //...etc...
    ];

    //Turns given flat arr into a tree and returns root..
    //(Assumes that no child is declared before parent)
    function makeTree(arr){
        //Array with all the children elements set correctly..
        var treeArr = new Array(arr.length);

        for(var i = 0, len = arr.length; i < len; i++){
            var arrI = arr[i];
            var newNode = treeArr[i] = {
                name: arrI[0],
                children: []
            };
            var parentI = arrI[1];
            if(parentI > -1){ //i.e. not the root..
                treeArr[parentI].children.push(newNode);
            }
        }
        return treeArr[0]; //return the root..
    }

    var root = makeTree(data);

要在更大的列表上测试速度,您可以运行:

    var data = [['root', -1]];
    for(var i = 1; i < 100000; i++){
        var parentI = Math.floor(Math.random()*(i-1));
        data.push(['a_name', parentI]);   
    }
    var start = new Date().getTime();
    var tree = makeTree(data);
    var end = new Date().getTime();

    console.log('Took: ' + (end-start) + 'ms.');

数组中有 100000 个元素,在我的旧桌面上需要

【讨论】:

  • 这只有在数据被排序的情况下才有效 :( 例如使用这样的数据:var data = [ ["South-East", 0], ["South-West", 0], ["Berkshire" , 1], ["Reading", 3], ["UK", -1] ]; 不起作用...
  • 嗯,它要求您不要在父母之前定义孩子,我认为这是合理的。我在上面添加了一条评论来表明这一点。首先对其进行排序相对容易,但这将是额外的处理。
【解决方案2】:

如果您有一个简单的 id 和 parent-id 对象数组而在关卡上没有其他线索,那么生成嵌套表单是一项艰巨的任务。我认为递归方法在长列表中不实用。到目前为止,我能想到的最好的方法是对数组进行排序,使所有孩子都在他们的父母之后。父母和孩子,甚至根对象都可以混合,但孩子必须在它的父母之后。假设对象结构类似于var data = [{id: KL442, pid: HN296}, {id: ST113, pid: HN296}, {id: HN296, pid: "root"},...] 但排序是工作的第一阶段。在排序时,我们可以生成一个 LUT(查找表)以几乎免费访问父母。在外循环的出口处,只需一条指令lut[a[i].id]=i; 就足够了。这使得工作在嵌套阶段非常快。这是排序和 LUT 准备阶段。

function sort(a){
  var len = a.length,
      fix = -1;
  for (var i = 0; i < len; i++ ){
      while(!!~(fix = a.findIndex(e => a[i].pid == e.id)) && fix > i) [a[i],a[fix]] = [a[fix],a[i]];
      lut[a[i].id]=i;
  }
  return a;
}

一旦对它进行排序,反向迭代是获得嵌套结构唯一要做的事情。这样您现在已经对数据数组进行了排序并准备好了 LUT,那么这就是嵌套代码。

for (var i = sorted.length-1; i>=0; i--)
    sorted[i].pid != "root" && (!! sorted[lut[sorted[i].pid]].children
                                && sorted[lut[sorted[i].pid]].children.push(sorted.splice(i,1)[0])
                                || (sorted[lut[sorted[i].pid]].children = [sorted.splice(i,1)[0]]));

对于工作示例,您可以查看a previous answer of mine

【讨论】:

    【解决方案3】:

    使用 Lodash:

    var index = _.mapKeys(data,'id');
    var obj = {};
    _.each(index,function(v){
      if(!v.parentId){
        obj[v.id]=v;
      }else{
        if(!index[v.parentId].children){
          index[v.parentId].children=[];
        }
        index[v.parentId].children.push(v);
      }
    });
    

    Demo is here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-04
      相关资源
      最近更新 更多