2013-11-27 4 views
5

У меня есть плоский объект и массив, из которого мне нужно построить древовидный объект.Как пройти через объект и создать дерево Object

choices: ['choice1', 'choice2', 'choice3']; 
items: [ 
    { 
     choice1: 'taste', 
     choice2: 'good', 
     choice3: 'green-lemon' 
    }, 
    { 
     choice1: 'taste', 
     choice2: 'bad', 
     choice3: 'green-lemon' 
    } 
]; 

Массив описывает уровень, на котором каждый выбор будет входить в дерево. Я не знаю, сколько вариантов, предметов или уровней будет позже.

Как получить следующий объект:

output: { 
    taste: { 
     good: { 
      green-lemon:1 
     }, 
     bad: { 
      green-lemon:1 
     } 
    } 
} 

Мне нужно, чтобы получить объект, описывающий, сколько элементов есть на каждом уровне. В этом примере это choice1: 1;choice2: 2 и каждый choice3: 1.

Любые советы о том, как построить петлю, чтобы получить этот результат?

+0

Есть ли будет больше уровней, чем это? Будет ли этот набор данных когда-либо расширяться? –

+0

Является ли массив 'choice' предназначен для описания уровня, на котором каждый выбор входит в дерево? Вопрос @ brainwipe также относится к правильному решению. –

+0

@brainwipe: может быть только 2 уровня выбора, и может быть 5 уровней выбора. Массив выбора и объект элементов никогда не будут меняться во время выполнения. – Dinkheller

ответ

2

Я думаю, что лучшим решением здесь является цикл с некоторой рекурсией. Я увеличил размер модели в примере, чтобы показать, что она идет с n уровнями. Проверьте выход с помощью консоли javascript.

var choices = ['choice1', 'choice2', 'choice3']; 
var items = [{ 
    choice1: 'taste', 
    choice2: 'good', 
    choice3: 'green-lemon' 
}, { 
    choice1: 'taste', 
    choice2: 'bad', 
    choice3: 'green-lemon' 
}, 
{ 
    choice1: 'taste', 
    choice2: 'ok', 
    choice3: 'green-lemon' 
}, 
{ 
    choice1: 'taste', 
    choice2: 'ok', 
    choice3: 'green-lemon' 
}]; 

function IsLastLevel(levelIndex) { 
    return (levelIndex == choices.length - 1); 
} 

function HandleLevel(currentItem, currentLevel, nextChoiceIndex) { 

    var nextLevelName = currentItem[choices[nextChoiceIndex]]; 

    if (typeof currentLevel[nextLevelName] === 'undefined') { 
     currentLevel[nextLevelName] = {}; 
    } 

    if (IsLastLevel(nextChoiceIndex)) { 
     if (currentLevel[nextLevelName] > 0) { 
      currentLevel[nextLevelName]++; 
     } else { 
      currentLevel[nextLevelName] = 1; 
     } 
    } else { 
     var goOneDeeper = nextChoiceIndex + 1; 
     HandleLevel(currentItem, currentLevel[nextLevelName], goOneDeeper); 
    } 
} 

var output = {}; 

for(var itemIndex in items) 
{ 
    var item = items[itemIndex]; 
    HandleLevel(item, output, 0); 
} 

console.log(output); 

JsFiddle Demo

+0

отлично. Большое спасибо. – Dinkheller

0

Можете ли вы прояснить, что отношения эти данные?

, где это значение «1» пришел отсюда: «зеленый-Леммон: 1»

что цель массива «выбор»?

+0

«1» описывает, что есть только один предмет с зеленым лимоном по вкусу -> хорошо. И один по вкусу -> плохо – Dinkheller

2

Brainwipe уже дал очень чистый ответ, но я думал, что я хотел бы попробовать мою руку в любом случае. Это решение работает рекурсивно, уменьшая список элементов, уровень за уровнем, до тех пор, пока он не достигнет листовых узлов.

function choiceTree(items, choices) { 
    // Return empty object if there were no choices. 
    if (!choices.length) return {}; 

    var choice = choices.shift(); 

    // Construct the current level of the tree. 
    var level = items.reduce(function(node, item) { 
     var value = item[choice]; 

     // Add item if branch node or set to 1 if leaf node. 
     node[value] = (choices.length) 
      ? (node[value] || []).concat(item) 
      : 1; 

     return node; 
    }, {}); 

    // Return if there are no remaining choices. 
    if (!choices.length) return level; 

    // Recursively construct the next level. 
    for (var node in level) 
     level[node] = choiceTree(level[node], choices.slice()); 

    return level; 
} 

jsFiddle demo

+0

Большое спасибо. Это намного меньше кода, но труднее читать. Я еще не работал с сокращением, но обязательно посмотрю на него. Благодарю. – Dinkheller

+2

'Array.reduce' действительно классный и недоиспользуемый, вы обязательно должны его прочитать! :) Все, что он делает, это «уменьшить» массив до одного значения; вы могли бы использовать вместо этого цикл 'for' (http://jsfiddle.net/Jordan/j5aXL/), но я думаю, что дополнительный код более беспорядочен. Он не поддерживается в IE 8 и ниже, но это [легко прошить] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#Compatibility). –

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