How to create nested child objects in Javascript from array?

5Responses

Write your response…

This answer has received 5 appreciations.

Edit

On a second thought, makeNestedObjWithArrayItemsAsKeys can further be simplified, and written concisely!

const makeNestedObjWithArrayItemsAsKeys = (arr) => {
  const reducer = (acc, item) => ({ [item]: acc });
  return arr.reduceRight(reducer, {});
};

cc: dvv.avinash Ivan Rahul Sidhant Siddarthan

The Object.assign in the original function (below) is unnecessary... have noticed it thanks to Jason's answer!

It'd be amiss on my part, to not mention that Jason's solution here (as I see it) is the most elegant one of the lot!


const makeNestedObjWithArrayItemsAsKeys = (arr) => {
  return arr.reduceRight(
    (accumulator, item) => {
      const newAccumulator = {};
      newAccumulator[item] = Object.assign(
        {},
        accumulator
      );
      return newAccumulator;
    },
    {}
  );
};

Given the following list:

const list = ["a", "b", "c"];

...the return value of makeNestedObjWithArrayItemsAsKeys(list) will be the following object:

{
  a: {
    b: {
      c: {}
    }
  }
}

Hope that answers your question! :)

+2 Beers for that function name! 🙌

Write a reply...

This answer has received 3 appreciations.

Whilst it's fun to see all the complex answers, mine's a bit more draconian... you probably won't find a faster routine for doing this. It exploits that objects are always passed by reference in JavaScript, even during assignment.

function nest(arr) {
    for (var obj = {}, ptr = obj, i = 0, j = arr.length; i < j; i++)
        ptr = (ptr[arr[i]] = {});
    return obj;
}

No recursive calls, no fancy mapping/each, no nested function calls/multiple functions, no long-winded objectAssign, no foreach or other methods that won't work in legacy browsers. Just a simple FOR loop and assignment. Should work all the way back to IE5.

since I'm obnoxious that way.

... plus that's your favourite browser :P

Write a reply...

This answer has received 2 appreciations.

let list = ['a', 'b', 'c']

let root = {}
let parent = null;

for(let el of list) {

    if(parent === null)
        parent = nest(el, root)
    else {
        parent = nest(el, parent);
    }
}

function nest(el, parent) {
    let node = {}

    parent[el] = node;

    return node;
}

console.log(root);

Output

{ a: { b: { c: {} } } }

The function nest() takes two parameters 1) el and 2) parent.

The first time nest is called we pass a (first element of the array) and root to the function.

Nest() creates a new object called node. Node then gets added to the parent parameter. Parent corresponds to root the first time the function is called. Then node is returned.

A reference of node is actually returned and not a copy (THIS IS IMPORTANT). Therefore, when nest() gets called a second time the parent parameter is actually a reference to the first node object that was created in the previously called nest() function.

Write a reply...

IMHO this question is too abstract to be of any transferrable value - you haven't said why you want to do this, though I suspect your example is simply too generic.

I'm not saying there isn't any value in such a reduction algorithm; it's just meaningless without context – anyone who answers has no guarantee they'll be providing something of practical use.

  • Where is the data coming from?
  • What value should be in the final nested result?
  • Will the structure already exist?
  • Can it take an input value?
  • Will it done by machines or humans?
  • Etc...

If all these are requirements, I suggest looking at Lodash set() :

Sets the value at path of object. If a portion of path doesn't exist, it's created. Arrays are created for missing index properties while objects are created for all other missing properties. Use _.setWith to customize path creation.

As a programming exercise, my take on it is:

function set(keys, value, obj) {
    obj = obj || {}
    keys = typeof keys === 'string'
        ? keys.match(/\w+/g)
        : Array.prototype.slice.apply(keys)
    keys.reduce(function(obj, key, index) {
        obj[key] = index === keys.length - 1 
            ? value 
            : typeof obj[key] === 'object' && obj !== null
                ? obj[key]
                : {}
        return obj[key]
    }, obj)
    return obj
}

// new object
var obj = set(['a', 'b', 'c'], 1)
//  { a: { b: { c: 1 } } } 

// existing objects
set(['b', 'c'], 2, obj)
// { a: { b: { c: 1 } }, b: { c: 2 } } 

// new props
set(['x', 'y'], 3, obj.b)
// { a: { b: { c: 1 } }, b: { c: 2, x: { y: 3 } } } 

// existing props
set(['x', 'z'], 4, obj.b)
// { a: { b: { c: 1 } }, b: { c: 2, x: { y: 3, z: 4 } } } 

// keys as string
set('x y z', 5, obj)
// { a: { b: { c: 1 } }, b: { c: 2, x: { y: 3, z: 4 } }, x: { y: { z: 5 } } }

obj

In general programming terms, usability and practicality is more valuable than brevity or speed.

That's why i asked here

Write a reply...

var list = ['a', 'b', 'c'];
var root = {};
list.forEach((item) => {
  var current = {};
  current[item] = {};
  var keys = Object.keys(current);
  var t = findRoot(root);
  t[keys[0]] = current[keys[0]];
});

function findRoot(obj) {
  var keys = Object.keys(obj);
  if (keys.length == 0) return obj;
  var lastKey = keys[keys.length - 1];
  return obj[lastKey];
}

console.log(root);
Write a reply...

Join a friendly and inclusive Q&A network for coders

  • 🖥Pick the technologies you like & read great content through your feed.
  • 💬Ask a question when you want to learn more about anything.
  • 🚀Share what you know & build your portfolio.
Sign up nowLearn more

loading ...