代码之家  ›  专栏  ›  技术社区  ›  Bondifrench

Javascript将对象数组转换为树

  •  2
  • Bondifrench  · 技术社区  · 9 年前

    我正在尝试转换此结构:

    var initial = [ 
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
    ];
    

    到这个:

    var example = {
    "Phase 1": {
        "Step 1": {
            "Task 1": { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" }, 
            "Task 2": { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" }
        } ,
        "Step 2": {
            "Task 1": { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" }, 
            "Task 2": { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" }
        }
    } ,
    "Phase 2": {        
        "Step 1": {
            "Task 1": { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" }, 
            "Task 2": { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
        } ,
        "Step 2": {
            "Task 1": { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" }, 
            "Task 2": { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }     
        }
    }
    };
    

    所以我可以很容易地提取这样的值 example['Phase 2']['Step 1']['Task 1']['Value']

    我已经使用 groupBy 功能如下:

    function groupBy(d, arr) {
    return arr.reduce(function(acc, i) {
        var p = i[d];
        var temp = acc[p] || [];
        temp.push(i);
        acc[p] = temp;
        return acc;
    }, {})
    }
    

    所以当我这么做的时候 var groupedByPhase = groupBy('Phase', initial); 我明白了 groupedByPhase :

    {
    "Phase 1" : [
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
    ],
    "Phase 2": [
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
    ] 
    }
    

    我还使用此功能按步骤分组:

    function groupByInNestedObj(item, obj) {
    var x = {};
    for (var i in obj) {
        x[i] = groupBy(item, obj[i]);
    }
    return x;
    }
    

    它可以在呼叫时获取 groupByInNestedObj('Step', groupBy('Phase', initial))

     {
    "Phase 1" : {
        "Step 1": [
        { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
        { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" }],
        "Step 2": [
        { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
        { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
        ]},
    "Phase 2": {
        "Step 1:" [
        { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
        { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" }],
        "Step 2:"[
        { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
        { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
        ]}
    }
    

    然而,我有点困在这里,做下一个。 理想情况下,我希望能够 groupBy("Task", groupBy("Step", groupBy("Phase", initial))) 因此groupBy在树的最深处分组。 欢迎任何建议!

    注: 我确实尝试了第二步来实现这个功能

    function groupByInNestedObj2 (item, obj) {
        var x = {};
        for(var i in obj) {
            for (var j in obj[i]) {
                x[i][j] = groupBy(item, obj[i][j]);
            }
        }
        return x;
    }
    

    但似乎不管用。

    注2: 前一个函数的第二个版本可以工作,但由于它修改了传递给它的对象,所以它并不纯粹

    function groupByInNestedObj2 (item, obj) {
        var x = {};
        for(var i in obj) {
            x[i] = obj[i]
            for (var j in x[i]) {
                x[i][j] = groupBy('Task', x[i][j]);
            }
        }
        return x;
    }
    

    所以当我这么做的时候 var groupByPhaseAndStepAndTask = groupByInNestedObj2('Task', groupByPhaseAndStep) groupByPhaseAndStep也被修改,这是一个不希望的副作用。仍在努力。

    3 回复  |  直到 9 年前
        1
  •  1
  •   Oleksandr T.    9 年前

    你可以尝试使用 .reduce 检查对象是否存在,如下图所示

    var initial = [ 
      { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
      { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
      { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
      { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
      { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
      { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
      { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
      { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
    ];
    
    var result = initial.reduce(function (prev, current) {
      prev[current.Phase] = prev[current.Phase] || {};
      prev[current.Phase][current.Step] = prev[current.Phase][current.Step] || {};
      prev[current.Phase][current.Step][current.Task] = current;
      return prev;
    }, {});
    
    console.log(JSON.stringify(result, null, 2));
        2
  •  1
  •   Cyril Cherian    9 年前

    我们可以用一个for循环来实现这一点:

    var initial = [ 
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
    ];
    var final = {}
    initial.forEach(function(d){
      if (!final[d.Phase]) //phase not present so make an object
      	final[d.Phase] = {};
      if (!final[d.Phase][d.Step]) //step not present so make an object
      	final[d.Phase][d.Step] = {};
      if (!final[d.Phase][d.Step][d.Task])//task not present so make an object and store the object
      	final[d.Phase][d.Step][d.Task] = d;
      
    })
    console.log(final)
        3
  •  0
  •   Community CDub    7 年前

    使用lodash和this的更通用的方法 answer :

    var initial = [
      { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
      { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
      { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
      { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
      { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
      { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
      { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
      { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
    ];
    
    _.mixin({
      groupByMulti: function(obj, values, context) {
        if (!values.length) return obj;
        var byFirst = _.groupBy(obj, _.head(values), context);
        for (var prop in byFirst) {
          byFirst[prop] = _.groupByMulti(byFirst[prop], _.tail(values), context);
        }
        return byFirst;
      }
    });
    
    var tree = _(initial).groupByMulti(['Phase', 'Step', 'Task']);
    $('#pre').append(JSON.stringify(tree, null, 3));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.0.0/lodash.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <pre id='pre'></pre>