Reputation: 43
This is in continuation to the answer posted for the question "Convert JavaScript dot notation object to nested object".
The code works like a charm but I'm unable to wrap my head around how!! So a few days later + a situation where my console.logs actually exceed my lines of code :P.. Here's my question:
Below code for JavaScript function:
function deepen(o) {
var oo = {}, t, parts, part;
for (var k in o) {
t = oo;
parts = k.split('.');
var key = parts.pop();
while (parts.length) {
part = parts.shift();
t = t[part] = t[part] || {};
}
t[key] = o[k]
}
return oo;
}
console.log(
deepen({ 'ab.cd.e' : 'foo', 'ab.cd.f' : 'bar', 'ab.g' : 'foo2' })
);
It deepens a JSON object from :
{ 'ab.cd.e' : 'foo', 'ab.cd.f' : 'bar', 'ab.g' : 'foo2' }
Into a nested object :
{ab: {cd: {e:'foo', f:'bar'}, g:'foo2'}}
I get the part where for each key value pair, the logic pops the last element post splitting into an array by ".".
That becomes the key.
What I'm not understanding is the below.
1) The function is returning 'oo' but the operations are all on 't'. The only relationship is that t is being assigned the'empty object' "oo" at the beginning of every iteration on the flat JSON.
2) after the "while (parts.length)" loop, oo miraculously has the nested structure whereas t has one level below it. if oo is assigned to t, how is that possible?
3) I don't see the function being called recursively. How is 00 getting nested beyond the first element of the flat JSON?
Upvotes: 2
Views: 129
Reputation: 21130
I'll first redefine the function with some better names, this way explanation is a lot easier to do.
function deepen(object) {
var nestedObject = {}, cursor, nestingPath, nodeKey;
for (var dotKey in object) {
cursor = nestedObject;
nestingPath = dotKey.split('.');
var leafKey = nestingPath.pop();
while (nestingPath.length) {
nodeKey = nestingPath.shift();
cursor = cursor[nodeKey] = cursor[nodeKey] || {};
}
cursor[leafKey] = object[dotKey];
}
return nestedObject;
}
My guess is that don't entirely know how the while loop functions. Important to know is that when two variables refer to the same object both change when you change one. They are the same object, but you've chosen to have two handles.
Let me provide an example:
object = {};
cursor = object;
cursor.foo = "bar";
object; //=> {foo: "bar"}
cursor; //=> {foo: "bar"}
cursor.a = {};
object; //=> {foo: "bar", a: {}}
cursor; //=> {foo: "bar", a: {}}
cursor = cursor.a;
object; //=> {foo: "bar", a: {}}
cursor; //=> {} <- this is ^
cursor.b = "c";
object; //=> {foo: "bar", a: {b: "c"}}
cursor; //=> {b: "c"}
The while loop is mostly based upon this principal. It's not easy to explain, but I hope the above clarifies things.
Another thing that might be confusing is the line:
cursor = cursor[nodeKey] = cursor[nodeKey] || {};
// read as
cursor = (cursor[nodeKey] = (cursor[nodeKey] || {}));
This could also be written as:
if (!cursor[nodeKey]) cursor[nodeKey] = {};
cursor = cursor[nodeKey];
This assigns a new object to the dynamic nodeKey
property if the property isn't there (falsy). Then cursor
is assigned to the nested object within, similar to my example above cursor = cursor.a
.
Upvotes: 1
Reputation: 53257
First, you're not working with JSON, but a JS object. Most of the time, you should see object as HashMap<String, HashMap<String, ...>>
ad infinitum, if you need a Java analogy.
Your questions:
t = oo
means they both refer to the same instance created at the start of the function. Why do you use a second variable?t = t[part]
You literally assign entry of t
to t
{'a.b':1, 'b.a':1}
. You don't need recursion though, you could use stack instead.Regarding your code:
for (var k in o)
is not a recommended approach to iterate over object entries. Use Object.entries
for(const part of parts)
would work just as wellUpvotes: 1