Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to loop through Object and create a tree Object

I have a flat object and an array from which I need to construct a tree-like object.

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

The array describes the level at which each choice will come in the tree. I do not know how many choices, items or levels there will be later.

How do I get the following object:

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

I need to get an object describing how many items there are on each level. In this example this is choice1: 1; choice2: 2 and each choice3: 1.

Any advice on how to build a loop to get this result?

like image 601
Dinkheller Avatar asked Oct 02 '22 07:10

Dinkheller


2 Answers

I think the best solution here is a loop with some recursion. I have increased the size of the model in the example to show it going with n levels. Check the output with your javascript console.

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

like image 111
Dr Rob Lang Avatar answered Oct 13 '22 10:10

Dr Rob Lang


Brainwipe has already given a very clean answer, but I thought I would try my hand anyway. This solution works by recursively reducing the list of items, level by level, until it reaches the leaf nodes.

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

like image 26
Jordan Gray Avatar answered Oct 13 '22 10:10

Jordan Gray