Reputation: 123
Can you think of a better way to remap this JavaScript object:
{
"id": 123,
"child.id": 456,
"child.child.id": 789
}
... into this JavaScript object:
{
"id": 123,
"child": {
"id": 456,
"child": {
"id": 789
}
}
}
There must be a shorter or faster solution. This is my attempt:
var original = {
"id": 123,
"child.id": 456,
"child.child.id": 789
};
var result = {};
Object.keys(original).forEach(function(key) {
var node = result;
var keys = key.match(/(\w+)/g);
for (var i = 0; i < keys.length; i++) {
if (!node[keys[i]]) {
node[keys[i]] = {};
}
if (i == keys.length - 1) {
node[keys[i]] = original[key];
} else {
node = node[keys[i]];
}
}
});
Upvotes: 2
Views: 2210
Reputation: 5151
I went with your original approach, but used recursion and it seems to be slightly faster in the broswers I've tested (except for safari and opera and they are exactly equal, not sure why that is..):
function remapObject(original){
var result = {};
Object.keys(original).forEach(function(key) {
function splitNode(node, nodes){
var name = nodes.shift()
node[name] = (nodes.length >= 1)
? splitNode(node[name] || {}, nodes)
: original[key];
return node;
}
splitNode(result, key.split("."));
});
return result;
}
var original = {
"id": 123,
"child.id": 456,
"child.other": 789
},
result = remapObject(original)
console.log(original);
console.log(result);
fiddle is here: http://jsfiddle.net/hyperthalamus/3A9NQ/ jsperf test is here: http://jsperf.com/mapping-by-keys
EDIT: I had to do something very similar to this recently (again) after spending a lot of time working with closures and optimization, etc. Below is a version that doesn't rely on Object.keys or array.forEach. array.forEach is generally not good for performance and both of these methods have limited browser support. The function below has been optimized for reuse as this function is used quite a bit in my app's initialization. I am posting this here (3 months later) because it suited my needs particularly well and I thought I'd share even though this is probably overkill.
var remapObject = (function () {
"use strict";
var nodeSplit = function (key, original) {
return function splitNode(node, nodes) {
var name = nodes.shift();
node[name] = (nodes.length >= 1) ? splitNode(node[name] || {}, nodes) : original[key];
return node;
};
};
return function (original) {
var result = {},
prop,
splitNode;
for (prop in original) {
if (original.hasOwnProperty(prop)) {
nodeSplit(prop, original)(result, prop.split("."));
}
}
return result;
};
}());
The function at the top is created once inside the initialization enclosure and returns functions to avoid creating a function inside a for loop as this is generally frowned on for optimization or so I'm told.
If the concern is file size, an compressed version is here:
var remapObject=function(){var h=function(a,b){return function g(d,e){var f=e.shift();d[f]=1<=e.length?g(d[f]||{},e):b[a];return d}};return function(a){var b={},c;for(c in a)a.hasOwnProperty(c)&&h(c,a)(b,c.split("."));return b}}();
Upvotes: 3