Fastest Way to Flatten/Un-Flatten Nested JavaScript Objects

Fastest way to flatten / un-flatten nested JavaScript objects

Here's my much shorter implementation:

Object.unflatten = function(data) {
"use strict";
if (Object(data) !== data || Array.isArray(data))
return data;
var regex = /\.?([^.\[\]]+)|\[(\d+)\]/g,
resultholder = {};
for (var p in data) {
var cur = resultholder,
prop = "",
m;
while (m = regex.exec(p)) {
cur = cur[prop] || (cur[prop] = (m[2] ? [] : {}));
prop = m[2] || m[1];
}
cur[prop] = data[p];
}
return resultholder[""] || resultholder;
};

flatten hasn't changed much (and I'm not sure whether you really need those isEmpty cases):

Object.flatten = function(data) {
var result = {};
function recurse (cur, prop) {
if (Object(cur) !== cur) {
result[prop] = cur;
} else if (Array.isArray(cur)) {
for(var i=0, l=cur.length; i<l; i++)
recurse(cur[i], prop + "[" + i + "]");
if (l == 0)
result[prop] = [];
} else {
var isEmpty = true;
for (var p in cur) {
isEmpty = false;
recurse(cur[p], prop ? prop+"."+p : p);
}
if (isEmpty && prop)
result[prop] = {};
}
}
recurse(data, "");
return result;
}

Together, they run your benchmark in about the half of the time (Opera 12.16: ~900ms instead of ~ 1900ms, Chrome 29: ~800ms instead of ~1600ms).

Note: This and most other solutions answered here focus on speed and are susceptible to prototype pollution and shold not be used on untrusted objects.

One liner to flatten nested object

Here you go:

Object.assign({}, ...function _flatten(o) { return [].concat(...Object.keys(o).map(k => typeof o[k] === 'object' ? _flatten(o[k]) : ({[k]: o[k]})))}(yourObject))

Summary: recursively create an array of one-property objects, then combine them all with Object.assign.

This uses ES6 features including Object.assign or the spread operator, but it should be easy enough to rewrite not to require them.

For those who don't care about the one-line craziness and would prefer to be able to actually read it (depending on your definition of readability):

Object.assign(
{},
...function _flatten(o) {
return [].concat(...Object.keys(o)
.map(k =>
typeof o[k] === 'object' ?
_flatten(o[k]) :
({[k]: o[k]})
)
);
}(yourObject)
)

Flatten nested objects, keeping the properties of parents

You could have a look to the arrays and if no objects inside return an object, otherwise map val property by storing other properties.

const
isObject = o => o && typeof o === 'object',
flat = array => {
if (!array.every(isObject)) return { val: array };
return array.flatMap(({ val, ...o }) => {
const temp = flat(val);
return Array.isArray(temp)
? temp.map(t => ({ ...o, ...t }))
: { ...o, ...temp };
});
},
data0 = [{ a: "x", val: [{ b: "y1", val: [1, 2, 3] }, { b: "y2", val: [4, 5, 6] }] }],
data1 = [{ a: "x", val: [{ b: "y1", val: [{ c: "z1", val: [1, 2] }] }, { b: "y2", val: [{ c: "z2", val: [3, 4] }, { c: "z3", val: [5, 6, 7] }, { c: "z4", val: [8] }] }] }];

console.log(flat(data0));
console.log(flat(data1))
.as-console-wrapper { max-height: 100% !important; top: 0; }

What's an efficient way to flatten/un-nest a nested object with an unknown level of nesting?

You could use (upcoming) Array#flatMap and get objects without children.

const untree = ({ children = [], ...data }) => [data, ...children.flatMap(untree)];

var tree = [{ title: "parent1", id: "1", children: [{ title: "child 1", id: 2, parentid: 1 }, { title: "child 2", id: 3, parentid: 1 }] }, { title: "parent 2", id: 4, children: [{ title: "child 1", id: 5, parentid: 4, children: [{ title: "GRAND CHILD", id: 6, parentid: 5 }] }] }],

result = tree.flatMap(untree);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Recursively flatten a deeply-nested mix of objects and arrays

Here's a simple recursion:

const deepKeys = (o) => 
Array .isArray (o)
? o .flatMap (deepKeys)
: Object (o) === o
? Object .entries (o) .flatMap (([k, v]) => [k, ... deepKeys (v)])
: []

const sampleData = {coyotes: '', armadillos: false, wombats: [''], geckos: 0, dogs: {beagle: '', bulldog: ''}, wolves: [{type: '', color: '', range: '', status: {endangered: true, protected: false}}], cats: {}}

console .log (deepKeys (sampleData))
.as-console-wrapper {max-height: 100% !important; top: 0}

Attempting to flatten nested objects based on a specific field key

You can combine Array#map and Array#reduce to achieve the desired output.

const mapped = data.map(({ scores, ...rest }) => ({
...rest,
...scores.reduce((output, score) => ({ ...output, [score.id]: score.score }), {})
}));

Angular: Flatten / un-flatten nested JSON objects

The answer is no. Angular doesn't have that, but RxJs could have something that you can find useful.

Best way to "flatten" an array inside an RxJS Observable

http://alanpryorjr.com/2019-05-15-rxjs-flattening-operators/

Best way to flatten JS object (keys and values) to a single depth array

You could just concat all keys and values. (It does not solve the type casting to number for keys.)

var object =  { 0: [1, 2, 3, 4] },

result = Object.keys(object).reduce(function (r, k) {

return r.concat(k, object[k]);

}, []);



console.log(result);

How to flatten a JavaScript object into a daisy chain like form?

You can create recursive function like this, and its important to store previous keys in one string.

var obj1 = {

firstName: 'John',

lastName: 'Green',

car: {

make: 'Honda',

model: 'Civic',

revisions: [

{ miles: 10150, code: 'REV01', changes: 0},

{ miles: 20021, code: 'REV02', changes: [

{ type: 'asthetic', desc: 'Left tire cap' },

{ type: 'mechanic', desc: 'Engine pressure regulator' }

] }

]

},

visits: [

{ date: '2015-01-01', dealer: 'DEAL-001' },

{ date: '2015-03-01', dealer: 'DEAL-002' }

]

};

function flatten(data, c) {

var result = {}

for(var i in data) {

if(typeof data[i] == 'object') Object.assign(result, flatten(data[i], c + '.' + i))

else result[(c + '.' + i).replace(/^\./, "")] = data[i]

}

return result

}

console.log(JSON.stringify(flatten(obj1, ''), 0, 4))


Related Topics



Leave a reply



Submit