2016-01-20 2 views
2

Я пытаюсь преобразовать эту структуру:Javascript преобразовать массив объектов в дереве

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" } 
    ]} 
} 

Однако я немного uck здесь, чтобы сделать следующий. В идеале я хотел бы иметь возможность делать 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 модифицирован тоже, что является нежелательным побочный эффект. Все еще работаю над этим.

ответ

0

Более общий способ использования lodash и это 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>

+0

Это довольно близко к желаемому результату, внутри задач объекты не массивы, но это было бы нормально. Хотя я использую lodash для некоторых вещей, я не слишком знаком с _.mixin, поэтому в вашем ответе почти слишком много магии. Кроме того, я стараюсь достичь состава функции, поэтому я могу сделать что-то вроде 'groupBy (« Task », groupBy (« Step », groupBy (« Phase », initial))) – Bondifrench

+0

Хорошо, мне удалось это сделать, рефакторинг вашего примера , используя простые функции head, tail и groupBy, мне нравится использование рекурсии, теперь просто нужно ее изменить, поэтому, когда array.length == 1, я получаю объект, а не массив с одним элементом – Bondifrench

1

Вы можете попробовать использовать .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));

1

Мы можем это сделать это с одним цикл:

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 
 
    \t final[d.Phase] = {}; 
 
    if (!final[d.Phase][d.Step]) //step not present so make an object 
 
    \t final[d.Phase][d.Step] = {}; 
 
    if (!final[d.Phase][d.Step][d.Task])//task not present so make an object and store the object 
 
    \t final[d.Phase][d.Step][d.Task] = d; 
 
    
 
}) 
 
console.log(final)

+0

Хотя это действительно дает правильный результат, что я пытаюсь достичь, на вершине результата, является композиция, см my_ groupBy («Task», groupBy («Step», groupBy («Phase», initial))) comment – Bondifrench

Смежные вопросы