DanHav Official
DanHav Official

Reputation: 43

Unlike Java, does an assignment operator in Javascript mean that all operations on the LHS are reflected for the RHS?

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

Answers (2)

3limin4t0r
3limin4t0r

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

Tom&#225;š Zato
Tom&#225;š Zato

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:

  1. t = oo means they both refer to the same instance created at the start of the function. Why do you use a second variable?
  2. t = t[part] You literally assign entry of t to t
  3. I didn't test the code, but I'm pretty sure it's buggy. Test what happens with object that have multiple names in top level, eg. {'a.b':1, 'b.a':1}. You don't need recursion though, you could use stack instead.

Regarding your code:

  1. Use descriptive names and comments, especially when asking question where other people need to understand your code
  2. Do not define all variables at the beginning of the function. That old habit comes from the dawn of C language and needs to die
  3. for (var k in o) is not a recommended approach to iterate over object entries. Use Object.entries
  4. There is no need to pop from parts array, it is reset every iteration. for(const part of parts) would work just as well

Upvotes: 1

Related Questions