3

I have an array, which is a list of keys: ['one', 'two', 'three'].

I need to generate an object, of the following format:

{
  path: 'one',
  nested: {
    path: 'two',
    nested: {
      path: 'three'
    }
  }
}

So far I've tried a couple of approaches, but they seem quite messy (I have one method which uses while(current = array.pop()) but it requires several conditionals to handle the first and last elements.

Is there a neater recursive strategy?

10
  • @Barmar I haven't found any answer that solves the problem recursively in both places, and I have purely recursive solution to share, please reopen this questionç Commented Jul 5, 2017 at 16:55
  • Why does it have to be recursive? Commented Jul 5, 2017 at 16:56
  • Because OP asks so Commented Jul 5, 2017 at 16:58
  • What is the expected result when the array is empty? Yes, your current structure does indeed require special handling of the last element, it would be better if it had something like nested: null. Commented Jul 5, 2017 at 17:07
  • @Bergi we would just add extra condition that checks for that and returns immediately empty object jsfiddle.net/6rpg5jc1 Commented Jul 5, 2017 at 17:11

5 Answers 5

6

You can use reduce() method and as accumulator pass object that you want add properties to.

var arr = ['one', 'two', 'three']
var obj = {}

arr.reduce(function(r, e, i) {
  r.path = e;
  return arr[i+1] ? r.nested = {} : r
}, obj)

console.log(obj)

If you want to use just recursion without loop you can create function like this.

var data = ['one', 'two', 'three']
var obj = {}

function makeObj(arr, n, o) {
  if (n == arr.length - 1) o.path = arr[n]
  else {
    o.path = arr[n];
    o.nested = {}
    makeObj(arr, n += 1, o.nested)
  }
  return o.nested
}

makeObj(data, 0, obj)
console.log(obj)

Sign up to request clarification or add additional context in comments.

1 Comment

Mutating the accumulator is absolutely ugly. Why not just use reduceRight instead?
3

var arr = ['one', 'two', 'three'];
var tree = arr.reduceRight((nested, path) => {
  return nested? {path, nested}: {path};
}, null);

console.log(tree);

or even better/simpler, just:

var arr = ['one', 'two', 'three'];
var tree = arr.reduceRight((nested, path) => ({path, nested}), null);

console.log(tree);

It simplifies things for the JS engine if all objects have the same hidden class (simplified: same property names).

2 Comments

reduceRight is the only correct appraoch here. +1!
The engine optimisation isn't really relevant. It mostly simplifies things in the code and for the programmer!
1
let arr = ['one', 'two', 'three'], obj = {};

obj['path'] = arr[arr.length - 1];
for(let i = arr.length - 2; i >= 0; i--) {
  obj = {
    path: arr[i],
    nested: obj
  };
}
console.log(obj);

3 Comments

it doesn't look recursive to me
Recursive solution will fail if array will contain too many elements. But if author needs only recursive solution, so be it ;)
How will a recursive solution fail?
0

You can use a decrementing for loop to solve this problem:

var props = ['one','two','three'],
    obj;
    
for(var a = props.length;a--;) {
    var temp = { path: props[a] };
    if(obj) {
        temp.nested = obj;
    }
    obj = temp;
}

console.log(obj);

Comments

-1

You can use Array.reduceRight() method:

function createNestedObject(keys, index = 0) {
  if (index >= keys.length) return null;
  
  return {
   path: keys[index],
   nested: createNestedObject(keys, index + 1)
  };
}

const keys = ['one', 'two', 'three'];
const result = createNestedObject(keys);
console.log(result);

2 Comments

Modified @Mr.Polywhirl

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.