How does one incrementally build a nested object structure from an array of property names?

132 Views Asked by At

My task is straight forward. I have an array of strings:

let a=["a","b","c"];

And i want to convert that array to (can alter the original array, doesn't matter) what i would like to call as "recursive object" just so:

//in json format just to demonstrate
"object": {
    "a": {
      "b":{
        "c":{
          
        }
        
      }
    }
  }

I've tried the following logic but couldn't get it to work due to reference problem and i couldn't build recursion.

let tempObj;
let obj2;

array.slice().reverse().forEach(function(item,index){
    
        obj2=tempObj;
        tempObj[item]="";
      
    });

Just to make sure we are on the same page, another example:

let arr=["alpha","beta","gamma"];
let magicObj=someMagicFunction(arr);
//magicObj["alpha"]["beta"]["gamma"] contains the value ""

Thanks

5

There are 5 best solutions below

6
Wendelin On BEST ANSWER

Here is a simple solution:

const arr = ["a","b","c"];
const result = arr.reverse().reduce((obj, key) => ({[key]: obj}), {})
console.log(result)

Here a little explaination:
o is the result of the last iteration and v is the current element in the array. {[v]: o} creates a new object and sets the property v to o and returns that.

0
DDomen On
let magicObj = arr.reverse().reduce((obj, prop) => ({ [prop]: obj }), {})
5
Peter Seliger On

Start aggregating your object from the array's right most side via reduceRight and provide e.g. an empty object / {} or an empty string / "" as this method's initial value(s) ...

console.log(
  'object :',
  ["a","b","c"]
    .reduceRight((obj, key) =>
      ({ [key]: obj }), {}
    )
);
console.log(
  'object :',
  ["alpha","beta","gamma"]
    .reduceRight((obj, key) =>
      ({ [key]: obj }), ""
    )
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

... and since code code-reuse always should be a goal the above examples change to ...

function createObjectWithParentKeyAndChildValue(value, key) {
  return { [key]: value };
}
console.log(
  'object :',
  ['a', 'b', 'c']
    .reduceRight(createObjectWithParentKeyAndChildValue, {})
);
console.log(
  'object :',
  ['alpha', 'beta', 'gamma']
    .reduceRight(createObjectWithParentKeyAndChildValue, '')
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

1
Peter On

there is my pure recursive answer:

let a=["a","b","c"];

const b = (arr = [], obj = null) => {
  if (arr.length > 0) {    
    const { length, [length - 1]: last, ...r } = arr;
    const rest = Object.values(r);
    const nextObj = obj ? { [last]: obj } : { [last]: {} };
    return b(rest, nextObj);
  }
  return obj;
};

console.log(b(a));
1
Scott Sauyet On

The reduce / reduceRight answers are great. But this can also be done with a fairly trivial recursive version:

const buildDeep = ([p, ...ps], val) =>
  p == undefined ? val : {[p]: buildDeep (ps, val)}

console .log (buildDeep (['a', 'b', 'c'], {}))
console .log (buildDeep (['alpha', 'beta', 'gamma'], ''))

To my mind, this is even simpler than reduce. It feels related to the various path-setting functions you see around, but is less complex since it doesn't have to work with an existing object.