4

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;
}
jorgepr
  • 167
  • 3
  • 15

6 Answers6

13

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.

marcinn
  • 1,746
  • 11
  • 13
  • 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). – Jonathan Gray Jan 23 '18 at 01:14
6

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/lodash@4.17.15/lodash.min.js"></script>
DannyFeliz
  • 742
  • 8
  • 16
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"));

Jonathan Gray
  • 2,439
  • 14
  • 20
1

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');
c69
  • 18,226
  • 6
  • 49
  • 81
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)

forgivenson
  • 4,323
  • 2
  • 17
  • 28
  • 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? – jorgepr Nov 13 '14 at 13:15
  • Linear time increase greater than the accepted answer if anyone is wondering. – mjwrazor Jul 21 '17 at 18:06
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)
  };
}
c2r0b
  • 1