Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

120
Views
Generate nesting object by path

I want to make function which takes object path, for example

{ 'person.data.info.more.favorite': 'smth' }

and returns nested object:

{
  person: {
    data: {
      info: {
        more: {
          favorite: 'smth',
        },
      },
    },
  },
}

What's the most simple method to do it?

about 4 years ago · Juan Pablo Isaza
3 answers
Answer question

0

If you only need to do a single key/value pair, a straightforward solution of nest using split and reduceRight -

const nest = ([path, value]) =>
  path.split('.').reduceRight((v, p) => ({ [p]: v }), value)

console.log(nest(['a', 'smth']))
console.log(nest(['a.b', 'smth']))
console.log(nest(['person.data.info.more.favorite', 'smth']))

{
  "a": "smth"
}
{
  "a": {
    "b": "smth"
  }
}
{
  "person": {
    "data": {
      "info": {
        "more": {
          "favorite": "smth"
        }
      }
    }
  }
}

If you need to apply this to an entire object where the key paths could possibly overlap with each other, we can write expand which depends on nest -

const input = {
  "server.host": "localhost",
  "server.port": 9999,
  "database.host": "localhost",
  "database.name": "mydb",
  "database.port": 7777,
  "database.user": "root",
  "debug.log": true
}

console.log(expand(input))
{
  "server": {
    "host": "localhost",
    "port": 9999
  },
  "database": {
    "host": "localhost",
    "name": "mydb",
    "port": 7777,
    "user": "root"
  },
  "debug": {
    "log": true
  }
}

We can write expand(o) to take Object.entries from the input object o and .map(nest) over each, and finally .reduce(merge) the result -

const expand = o =>
  Object.entries(o).map(nest).reduce(merge, {})

const nest = ([path, value]) =>
  path.split(".").reduceRight((v, p) => ({ [p]: v }), value)

const merge = (left = {}, right = {}) =>
  Object
    .entries(right)
    .map(([ k, v ]) =>
      isobject(v) && isobject(left[k])
        ? [ k, merge(left[k], v) ]
        : [ k, v ]
    )
    .reduce(assign, left)
      
const assign = (o, [ k, v ]) =>
  Object.assign(o, { [k]: v })

const isobject = t =>
  t?.constructor === Object   
    
const input = {
  "server.host": "localhost",
  "server.port": 9999,
  "database.host": "localhost",
  "database.name": "mydb",
  "database.port": 7777,
  "database.user": "root",
  "debug.log": true
}

console.log(expand(input))

about 4 years ago · Juan Pablo Isaza Report

0

EDIT: Here is the correct answer sorry I misread it the first time.

let myObj = { 'person.data.info.more.favorite': 'smth' }

function getNestedObj( object ) {

  let keys = Object.keys( object )[0];
  let value = Object.values( object )[0];
  let keyArray = keys.split(".");

  let nextObject;
  let returnObj = {};
  

  keyArray.forEach( ( key ) => {
 
    let newObj = {};

    if( !nextObject ) {
      returnObj[key] = newObj
      nextObject = newObj;
    } else {
      nextObject[key] = newObj
      nextObject = newObj;
    }
    console.log(key)
  });

  nextObject[0] = value

  console.log( returnObj );

  return returnObj;
}

getNestedObj( myObj);

OLD ANSWER THAT READ QUESTION WRONG:

Hey you can accomplish this with the below code. It will only work with string values though with this implementation

"use strict";
let myObject = {
    person: {
        data: {
            info: {
                more: {
                    favorite: 'smth',
                },
                less: {
                    favourite: 'smthing else'
                },
                random: "a string"
            },
        },
    },
};
function concatKeys(object, key) {
    let keys = Object.keys(object);
    let keyDictionary = {};
    if (key && typeof object === 'string') {
        return { [key]: object };
    }
    if (keys.length > 0) {
        keys.forEach((nextKey) => {
            let newKey = key ? `${key}.${nextKey}` : nextKey;
            let nextObj = object[nextKey];
            let newDictionary = concatKeys(nextObj, newKey);
            keyDictionary = Object.assign(Object.assign({}, keyDictionary), newDictionary);
        });
    }
    return keyDictionary;
}
let output = concatKeys(myObject);
console.log(output);

You can see it working here Typescript playground

Running the above code will log this to the console.

[LOG]: {
  "person.data.info.more.favorite": "smth",
  "person.data.info.less.favourite": "smthing else",
  "person.data.info.random": "a string"
} 

Edit: Sorry I realize now you asked for this in javascript updated the answer to be in JS but the playground is still typescript but the above is JS.

about 4 years ago · Juan Pablo Isaza Report

0

This version has a similar idea to the answer from Mulan, but with a different breakdown in responsibilities. It goes through a useful intermediate format that I've been calling pathEntries, a parallel to the result of Object.entries output, but containing full paths rather than just top-level properties. For this example, it would be just

[['person', 'data', 'info', 'more', 'favorite'], 'smth']

Our expand function calls Object .entries on your input, splits the keys on dots, and then calls hydrate on the result. hydrate is in my regular utility belt and is just a thin wrapper around setPath, folding the path entries into a single object. It looks like this:

const setPath = ([p, ...ps]) => (v) => (o) =>
  p == undefined ? v : Object .assign (
    Array .isArray (o) || Number .isInteger (p) ? [] : {},
    {...o, [p]: setPath (ps) (v) ((o || {}) [p])}
  )

const hydrate = (xs) =>
  xs .reduce ((a, [p, v]) => setPath (p) (v) (a), {})

const expand  = (o) =>
  hydrate (Object .entries (o) .map (([k, v]) => [k.split ('.'), v]))

console .log (expand ({'person.data.info.more.favorite': 'smth'}))

const input = {"server.host": "localhost", "server.port": 9999, "database.host": "localhost", "database.name": "mydb", "database.port": 7777, "database.user": "root", "debug.log": true}

console .log (expand (input))
.as-console-wrapper {max-height: 100% !important; top: 0}

One useful thing about this intermediate format is that it can also represent arrays, using integers for array indices and strings for object properties:

hydrate ([
  [[0, 'foo', 'bar', 0, 'qux'], 42], 
  [[0, 'foo', 'bar', 1, 'qux'], 43], 
  [[0, 'foo', 'baz'], 6],
  [[1, 'foo', 'bar', 0, 'qux'], 44], 
  [[1, 'foo', 'bar', 1, 'qux'], 45], 
  [[1, 'foo', 'baz'], 7],  
]) //=> 
// [
//   {foo: {bar: [{qux: 42}, {qux: 43}], baz: 6}}, 
//   {foo: {bar: [{qux: 44}, {qux: 45}], baz: 7}}
// ]

But to use that feature here, we would probably need a more sophisticated string format, something like { '[0].foo.bar[0].qux': 42 } and we'd need a somewhat more sophisticated parser than just k .split ('.'). This is not difficult, but would take us further afield, as would dealing with the still more sophisticated system that allows for arbitrary string keys, including quotation marks, brackets, and periods.

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!