snippetjavascriptMinor
JavaScript function to generate tree object from flat object
Viewed 0 times
fromjavascriptfunctionflatgenerateobjecttree
Problem
The following function takes an object of items in the format of
where
The function
Examples
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
{
'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
This translates to the following JS:
Now, creating the whole object is just a matter of applying it to all the keys passed in:
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.