7

I've got an empty object and a string:

var obj = {};
var str = "a.b.c";

Is there a way I can turn this into

obj = { a: { b: { c: { } } } }

I can't quite wrap my head around this one and I'm not even sure if it would be possible.

user977433
  • 73
  • 1
  • 4
  • 2
    What's your scenario and final goal? I suspect that there is a better way to achieve your ultimate goal rather than trying to do something like this. – Darin Dimitrov Oct 03 '11 at 21:16
  • is this an interview question? – Yevgeny Simkin Oct 03 '11 at 21:16
  • There is something similar in YUI to create objects. I'll see if I can find it. – BNL Oct 03 '11 at 21:21
  • possible duplicate of [Building object hierarchy from a 'namespace' string](http://stackoverflow.com/questions/2308783/building-object-hierarchy-from-a-namespace-string) – Felix Kling Oct 03 '11 at 21:23
  • Nearly everything is possible with JavaScript; some things are just far less efficient... – beatgammit Oct 03 '11 at 21:26
  • @Felix - yes, duplicate, but is the answer given there, a good answer? – mplungjan Oct 03 '11 at 21:27
  • Haha no it's not an interview question but wow if I ever interview anyone I may use this for a question. I've got a form with data associated with each input field that "looks.like.this" and I need to build a JSON object to return back to the server. Great suggestions here will try some when I return to work tomorrow – user977433 Oct 04 '11 at 00:29

5 Answers5

10
var obj = {};
var str = "a.b.c";
var arr = str.split('.');
var tmp = obj;

for (var i=0,n=arr.length; i<n; i++){
   tmp[arr[i]]={};
   tmp = tmp[arr[i]];
}

ES6:

let str = "a.b.c",
    arr = str.split('.'),
    obj, o = obj = {};

arr.forEach(key=>{o=o[key]={}});

console.log(obj);

ES6/Reduced (array storage unnecessary):

let str = "a.b.c", obj, o = obj = {};

str.split('.').forEach(key=>o=o[key]={});

console.log(obj);

ES6/Array.prototype.reduce:

let str = "a.b.c", last;

let obj = str.split('.').reduce((o, val) => {
  if (typeof last == 'object')
    last = last[val] = {};
  else
    last = o[val] = {};

  return o;
}, {});

console.log(obj);
vol7ron
  • 40,809
  • 21
  • 119
  • 172
  • 1
    Such a brilliant and elegantly simple solution. What I had was very close to this but I did not initially set tmp = obj which created the issue. Thank you for the help! – user977433 Oct 04 '11 at 12:39
  • @user977433: I suspect inside the loop you could condense it to `tmp = tmp[arr[i]] = {}` and get rid of the curly braces. – vol7ron Oct 04 '11 at 14:38
  • @vol7ron you mean exactly like my answer 30 minutes before yours? – David Hellsing Oct 05 '11 at 07:36
  • @vol7ron whatever, your answer is almost identical to mine except more verbose. You even used the same variable names. And check the time, it’s posted exactly 30 minutes after. Kind of defeats the whole purpose of SO if you ask me... – David Hellsing Oct 05 '11 at 14:32
  • @David: The fact that you have deducted a point for an answer, out of spite, kind of defeats the whole purpose of SO, if you ask me... – vol7ron Oct 05 '11 at 14:59
  • @vol7ron I deducted a point because your where copying / not searching the other answers before posting an almost identical duplicate. And over here 21:48 (my edit) comes before 22:12. Besides, my edit just replaced the for loop with a while, since it’s cleaner. Moving on... – David Hellsing Oct 05 '11 at 15:18
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/4039/discussion-between-vol7ron-and-david) – vol7ron Oct 05 '11 at 16:12
3

This is from the yui2 yahoo.js file.

YAHOO.namespace = function() {
  var a=arguments, o=null, i, j, d;
  for (i=0; i<a.length; i=i+1) {
      d=(""+a[i]).split(".");
      o=YAHOO;

      // YAHOO is implied, so it is ignored if it is included
      for (j=(d[0] == "YAHOO") ? 1 : 0; j<d.length; j=j+1) {
          o[d[j]]=o[d[j]] || {};
          o=o[d[j]];
      }
  }

  return o;
};

See the source for documentation.

https://github.com/yui/yui2/blob/master/src/yahoo/js/YAHOO.js

BNL
  • 7,085
  • 4
  • 27
  • 32
3

This recursive function returns you the string representation of the desired object

//Usage: getObjectAsString('a.b.c'.split(/\./))
function getObjectAsString (array){
   return !array.length ? '{}' 
             : '{"' + array[0] + '":' + getObjectAsString (array.slice(1)) + '}';
}

Now you can convert the output of getObjectAsString into object using

JSON.parse(getObjectAsString('a.b.c'.split(/\./)))

EDIT: Removed 'Input as String' version as it works only for single letter subparts in the namespace such as the one given in the question (a.b.c) which is generally not the case.

Narendra Yadala
  • 9,554
  • 1
  • 28
  • 43
0

Here you go:

var string = "a.b.c",
   array = string.split('.');
JSON.parse("{\"" + array.join('": {\"') + "\": {" +array.map(function () {return '}'}).join('') + "}")

Example

Joe
  • 80,724
  • 18
  • 127
  • 145
  • that's a very interesting approach. I'm curious though, what makes my solution (which is a lot easier to read) less useful than this or the one above with the upvote. Isn't there something to be said for code clarity and simplicity? – Yevgeny Simkin Oct 03 '11 at 21:38
  • @Dr.Dredel, I hear ya, man, but there's a general aversion to `eval()` in javascript amongst SO'ers. Really, since you're controlling the code given to `eval()`, the main drawback of its use in your solution is the hit to performance. FYI: http://stackoverflow.com/questions/197769/when-is-javascripts-eval-not-evil – Jonathan M Oct 03 '11 at 21:45
  • 1
    @Johathan M. Thanks! I clicked on that link expecting to be told that I'm a total jack-ass for using eval, but it's a very sober and rational examination which actually clearly states that using eval isn't all that much more tasking and should be used pretty much exactly as I used it (to reduce code clutter, if possible). As I said, my solution was basically just a hack, and it was the first answer given to a question the purpose of which still eludes me. In general I find SOers are frequently petty and eager to downvote things that can easily simply be ignored. – Yevgeny Simkin Oct 03 '11 at 23:20
0

Here's my take on it:

function ensureKeys(str, obj) {
    for(var parts = str.split('.'), i=0, l=parts.length, cache=obj; i<l; i++) {
        if(!cache[parts[i]]) { 
            cache[parts[i]] = {};
        }
        cache = cache[parts[i]];
    }

    return obj;
}

var obj = {};
ensureKeys('a.b.c', obj);
// obj = { a: { b: { c: {} } } }
Marshall
  • 4,716
  • 1
  • 19
  • 14