How to unflatten a JavaScript object in a daisy-chain/dot notation into an object with nested objects and arrays?
You can first use for...in
loop to loop object properties, then split each key at .
then use reduce to build nested properties.
var obj2 = {"firstName":"John","lastName":"Green","car.make":"Honda","car.model":"Civic","car.revisions.0.miles":10150,"car.revisions.0.code":"REV01","car.revisions.0.changes":"","car.revisions.1.miles":20021,"car.revisions.1.code":"REV02","car.revisions.1.changes.0.type":"asthetic","car.revisions.1.changes.0.desc":"Left tire cap","car.revisions.1.changes.1.type":"mechanic","car.revisions.1.changes.1.desc":"Engine pressure regulator","visits.0.date":"2015-01-01","visits.0.dealer":"DEAL-001","visits.1.date":"2015-03-01","visits.1.dealer":"DEAL-002"}
function unflatten(data) {
var result = {}
for (var i in data) {
var keys = i.split('.')
keys.reduce(function(r, e, j) {
return r[e] || (r[e] = isNaN(Number(keys[j + 1])) ? (keys.length - 1 == j ? data[i] : {}) : [])
}, result)
}
return result
}
console.log(unflatten(obj2))
Try breaking the problem down into two distinct challenges:
- Setting a value by path
- Looping over an object and unflattening the keys one by one
You might start with a setIn
function that would look something like this:
function setIn(path, object, value) {
let [key, ...keys] = path;
if (keys.length === 0) {
object[key] = value;
} else {
let nextKey = keys[0];
object[key] = object[key] || isNaN(nextKey) ? {} : [];
setIn(keys, object[key], value);
}
return object;
}
Then combine it with an unflatten
function which loops over an object running setIn
for each key.
function unflatten(flattened) {
let object = {};
for (let key in flattened) {
let path = key.split('.');
setIn(path, object, flattened[key]);
}
return object;
}
Of course, there's already an npm package for doing this, and it'd also it'd be easy to implement your own using functions like _.set
from lodash.
It's unlikely that you'd ever run into a long enough path that you'd end up running out of stack frames, but of course it's possible to implement setIn
without recursion, using loops or trampolines.
And finally, if immutable data is your thing and you want to work with a version of setIn
that doesn't modify your data structures, then you could take a look at the implementation in Zaphod—a JavaScript library for treating the native data structures as though they were immutable.