HiveBrain v1.2.0
Get Started
← Back to all entries
snippetjavascriptMinor

JavaScript function to generate tree object from flat object

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
fromjavascriptfunctionflatgenerateobjecttree

Problem

The following function takes an object of items in the format of

{
  'complex-key': 'value',
  // Repeat
}


where complex-key is a key delimited by dots, and returns an object in the form of a tree, where you can access the value by traversing the parts of the complex keys as keys in the object (see examples below).

The function

function createMessages(items) {
    var result = {};
    Object.keys(items).forEach(function(key) {
        var keyParts = key.split('.');
        var currentObj = result;
        while (keyParts.length > 0) {
            var currentKey = keyParts.shift();
            if (keyParts.length === 0) {
                var currentItems = {};
                if (currentObj[currentKey]) {
                    currentItems = currentObj[currentKey];
                }
                currentObj[currentKey] = function() { return items[key]; };
                Object.keys(currentItems).forEach(function(currentItemKey) {
                    currentObj[currentKey][currentItemKey] = currentItems[currentItemKey];
                });
            }
            else if (!currentObj[currentKey]) { currentObj[currentKey] = {}; }
            currentObj = currentObj[currentKey];
        }
    });
    return result;
}


Examples

var message1 = createMessages({'a.b.c.d': 42});
console.log(message1.a.b.c.d() === 42); // true

var message2 = createMessages({'a.b.c.d': 42, 'a.b.c.e': 43});
console.log(message2.a.b.c.d() === 42); // true
console.log(message2.a.b.c.e() === 43); // true

var message3 = createMessages({'a.b.c.d': 42, 'a.b.c': 43});
console.log(message3.a.b.c() === 43); // true
console.log(message3.a.b.c.d() === 42); // true (properties on the functions)


The current function looks overly bloated and inefficient (with \$O(n\times m)\$ at best, and \$O(n\times m^2)\$ at worst). While this is going to be used for test code only, and to small objects only (no more than 10 keys), I would still want to find a way to make it prettier an

Solution

Here is what I'd do. I'd split it into two actions - adding a single property and assigning them all.

Adding a single property is just adding a function if it's something like "a", and it's adding a property to a sub-object if it's something like "a....". So let's use recursion and break it up into those two use cases:

  • If it's at length one , create the function (but keep all the keys for the third example).



  • If it's more, call it recursively and create a property.



This translates to the following JS:

function walkTheChain(arr, val, obj){
  if(arr.length === 1) return obj[arr[0]] = Object.assign(() => val, obj[arr[0]]);
  if(!(arr[0] in obj)) obj[arr[0]] = {};
  walkTheChain(arr.slice(1), val, obj[arr[0]]);
}


Now, creating the whole object is just a matter of applying it to all the keys passed in:

function createMessages(map){
  var obj = {};
  for(var key in map) walkTheChain(key.split("."), map[key], obj);
  return obj;
}

Code Snippets

function walkTheChain(arr, val, obj){
  if(arr.length === 1) return obj[arr[0]] = Object.assign(() => val, obj[arr[0]]);
  if(!(arr[0] in obj)) obj[arr[0]] = {};
  walkTheChain(arr.slice(1), val, obj[arr[0]]);
}
function createMessages(map){
  var obj = {};
  for(var key in map) walkTheChain(key.split("."), map[key], obj);
  return obj;
}

Context

StackExchange Code Review Q#106143, answer score: 8

Revisions (0)

No revisions yet.