7

I'm trying to create a JS object dynamically providing a key and a value. The key is in dot notation, so if a string like car.model.color is provided the generated object would be:

{
  car: {
    model: {
      color: value;
    }
  }
}

The problem has a trivial solution if the key provided is a simple property, but i'm struggling to make it work for composed keys.

My code:

function (key, value) {
  var object = {};
  var arr = key.split('.');                                   
  for(var i = 0; i < arr.length; i++) {
    object = object[arr[i]] = {};
  }
  object[arr[arr.length-1]] = value;
  return object;
}
2
  • What do u actually mean by more complex strings? Could you plz share what you have tried? Commented Nov 13, 2014 at 12:36
  • Simple: "property", complex: "property1.property2.property3". Sorry if i didn't make it clear. Commented Nov 13, 2014 at 12:37

6 Answers 6

14

your slightly modified code

function f(key, value) {
  var result = object = {};
  var arr = key.split('.');                                   
  for(var i = 0; i < arr.length-1; i++) {
    object = object[arr[i]] = {};
  }
  object[arr[arr.length-1]] = value;
  return result;
}

In the loop you should set all of the props but the last one. Next set the final property and all set.

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

1 Comment

That has the one caveat of possibly replacing a variable called object outside of the function's scope. The first line in the function is the equivalent of assigning {} to object and result without initializing it first as a variable within scope as was done with result (most likely resulting in object ending up in the global scope).
9

If you're using lodash you could use _.set(object, path, value)

const obj = {}
_.set(obj, "car.model.color", "my value")
console.log(obj)
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

If you don't have lodash in your project, you can install this single function lodash.set.

Comments

2

Use namespace pattern, like the one Addy Osmani shows: http://addyosmani.com/blog/essential-js-namespacing/

Here's the code, pasted for convenience, all credit goes to Addy:

// top-level namespace being assigned an object literal
var myApp = myApp || {};
// a convenience function for parsing string namespaces and
// automatically generating nested namespaces
function extend( ns, ns_string ) {
    var parts = ns_string.split('.'),
        parent = ns,
        pl, i;
    if (parts[0] == "myApp") {
        parts = parts.slice(1);
    }
    pl = parts.length;
    for (i = 0; i < pl; i++) {
        //create a property if it doesnt exist
        if (typeof parent[parts[i]] == 'undefined') {
            parent[parts[i]] = {};
        }
        parent = parent[parts[i]];
    }
    return parent;
}
// sample usage:
// extend myApp with a deeply nested namespace
var mod = extend(myApp, 'myApp.modules.module2');

Comments

2
function strToObj(str, val) {
var i, obj = {}, strarr = str.split(".");
var x = obj;
    for(i=0;i<strarr.length-1;i++) {
    x = x[strarr[i]] = {};
    }
x[strarr[i]] = val;
return obj;
}

usage: console.log(strToObj("car.model.color","value"));

Comments

1

I would use a recursive method.

var createObject = function(key, value) {
    var obj = {};
    var parts = key.split('.');
    if(parts.length == 1) {
        obj[parts[0]] = value;
    } else if(parts.length > 1) {
        // concat all but the first part of the key
        var remainingParts = parts.slice(1,parts.length).join('.');
        obj[parts[0]] = createObject(remainingParts, value);
    }
    return obj;  
};

var simple = createObject('simple', 'value1');
var complex = createObject('more.complex.test', 'value2');

console.log(simple);
console.log(complex);

(check the console for the output)

2 Comments

Other than recursivity, which are advantages of this code? You have to split and concatenate the remaining parts with each iteration, could it affect performance?
Linear time increase greater than the accepted answer if anyone is wondering.
0

Here's a recursive approach to the problem:

const strToObj = (parts, val) => {
  if (!Array.isArray(parts)) {
    parts = parts.split(".");
  }
  if (!parts.length) {
    return val;
  }
  return {
    [parts.shift()]: strToObj(parts, val)
  };
}

Comments

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.