var flattenObject = function(ob) { | |
var toReturn = {}; | |
for (var i in ob) { | |
if (!ob.hasOwnProperty(i)) continue; | |
if ((typeof ob[i]) == 'object') { | |
var flatObject = flattenObject(ob[i]); | |
for (var x in flatObject) { | |
if (!flatObject.hasOwnProperty(x)) continue; | |
toReturn[i + '.' + x] = flatObject[x]; | |
} | |
} else { | |
toReturn[i] = ob[i]; | |
} | |
} | |
return toReturn; | |
}; |
This comment has been minimized.
This comment has been minimized.
Many thanks for this snippet! |
This comment has been minimized.
This comment has been minimized.
line 7 will flatten an Array (typeof [] => "object"), you may want to use toString.call(ob[i]) instead if you want the actual array, instead of it being flattened too. |
This comment has been minimized.
This comment has been minimized.
Thanks! Just what I was looking for! |
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
This is awesome, nice work! I ended up making one small mod on line 7 to flatten keys carrying null values: if ((typeof ob[i]) == 'object' && ob[i] !== null) { (since typeof null == "object") |
This comment has been minimized.
This comment has been minimized.
Nice! Just what I was looking for. |
This comment has been minimized.
This comment has been minimized.
Super helpful. Thank you! |
This comment has been minimized.
This comment has been minimized.
many thanks sir |
This comment has been minimized.
This comment has been minimized.
thank you sir. |
This comment has been minimized.
This comment has been minimized.
Definitely agree with @dustinlarimer. It's also generally good practice to use |
This comment has been minimized.
This comment has been minimized.
pure awesomesauce :: ty i forked @ https://gist.github.com/gdibble/9e0f34f0bb8a9cf2be43 w/ this behavior:
|
This comment has been minimized.
This comment has been minimized.
This only goes one level deep. This doesn't flatten as expected |
This comment has been minimized.
This comment has been minimized.
This silently fails for |
This comment has been minimized.
This comment has been minimized.
If you want arrays and date objects to be ignored you should use: OK way to do it (if you know you will only go across arrays, objects and Date objects): Object.prototype.toString.call( obj[i] ) === '[object Object]'
// Because
Object.prototype.toString.call( [] ) // returns "[object Array]"
Object.prototype.toString.call( new Date() ) // returns "[object Date]"
Object.prototype.toString.call( {} ) // returns "[object Object]" Better way (if you need to differentiate by type more than just arrays, objects and Date object): var obj = {};
obj.constructor === Object; // will return true
// So if we have something like
function Test() {}
var obj = new Test();
obj.constructor === Test // will return true
// Sweet! We can detect if it is an object with custom constructor
// Whereas
Object.prototype.toString.call( obj ) === "[object Object]" // will return true
// which is just not OK if there are object with custom constructor functions |
This comment has been minimized.
This comment has been minimized.
Many thanks for this cool snippet. |
This comment has been minimized.
This comment has been minimized.
Beautiful!!! |
This comment has been minimized.
This comment has been minimized.
Modified version in coffee:
|
This comment has been minimized.
This comment has been minimized.
as the documentation says :
Then I use it to write another version of the method without unnecessary hasOwnProperty checks, it works with String, int, object, array, array of object, null, undefined, you can check this jsbin :
|
This comment has been minimized.
This comment has been minimized.
Might I recommend updating this to ES6? |
This comment has been minimized.
This comment has been minimized.
I created this as an ES6 closure: /**
* PRIVATE
* Flatten a deep object into a one level object with it’s path as key
*
* @param {object} object - The object to be flattened
*
* @return {object} - The resulting flat object
*/
const flatten = object => {
return Object.assign( {}, ...function _flatten( objectBit, path = '' ) { //spread the result into our return object
return [].concat( //concat everything into one level
...Object.keys( objectBit ).map( //iterate over object
key => typeof objectBit[ key ] === 'object' ? //check if there is a nested object
_flatten( objectBit[ key ], `${ path }/${ key }` ) : //call itself if there is
( { [ `${ path }/${ key }` ]: objectBit[ key ] } ) //append object with it’s path as key
)
)
}( object ) );
}; Call it via: const flat = flatten( realDeepObject ); Test case: const realDeepObject = {
level1: {
level2: {
level3: {
more: 'stuff', //duplicate key
other: 'stuff',
level4: {
the: 'end',
},
},
},
level2still: {
last: 'one',
},
am: 'bored',
},
more: 'stuff', //duplicate key
ipsum: {
lorem: 'latin',
},
};
const flat = flatten( realDeepObject );
console.log( flat ); Output: { '/level1/level2/level3/more': 'stuff',
'/level1/level2/level3/other': 'stuff',
'/level1/level2/level3/level4/the': 'end',
'/level1/level2still/last': 'one',
'/level1/am': 'bored',
'/more': 'stuff',
'/ipsum/lorem': 'latin' } (You can replace the separators |
This comment has been minimized.
This comment has been minimized.
what would I have to change so that the first separator is removed? { 'level1/level2/level3/more': 'stuff', |
This comment has been minimized.
This comment has been minimized.
@afrozl you can modify it like this _flatten( objectBit, path = 'deepObject' ) |
This comment has been minimized.
This comment has been minimized.
Tyepscript version private flattenObject(ob:any): any {
const toReturn = {};
for (const key in ob) {
if (!ob.hasOwnProperty(key)) {
continue;
}
if ((typeof ob[key]) === 'object') {
const flatObject = this.flattenObject(ob[key]);
for (const key2 in flatObject) {
if (!flatObject.hasOwnProperty(key2)) {
continue;
}
// this.logger.debug(`adding ${key + '.' + key2}:${flatObject[key2]}`);
toReturn[key + '.' + key2] = flatObject[key2];
}
} else {
// this.logger.debug(`adding ${key}:${ob[key]}`);
toReturn[key] = ob[key];
}
}
return toReturn;
}; |
This comment has been minimized.
This comment has been minimized.
Hi, I've changed the version of @dominikwilkowski slightly: function flatten(object, separator = '.') {
return Object.assign({}, ...function _flatten(child, path = []) {
return [].concat(...Object.keys(child).map(key => typeof child[key] === 'object'
? _flatten(child[key], path.concat([key]))
: ({ [path.concat([key]).join(separator)] : child[key] })
));
}(object));
} |
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
Here is an improved version of @danieldietrich in function flatten(object: object, separator = '.'): object {
const isValidObject = (value): boolean => {
if (!value) { return false; }
const isArray = Array.isArray(value);
const isBuffer = Buffer.isBuffer(value);
const isΟbject = Object.prototype.toString.call(value) === "[object Object]";
const hasKeys = !!Object.keys(value).length;
return !isArray && !isBuffer && isΟbject && hasKeys;
};
return Object.assign({}, ...function _flatten(child, path = []) {
return [].concat(...Object.keys(child)
.map(key => isValidObject(child[key])
? _flatten(child[key], path.concat([key]))
: { [path.concat([key]).join(separator)]: child[key] }));
}(object));
} |
This comment has been minimized.
This comment has been minimized.
@JimiC I had some errors and when using your version, regarding implicit any returns and the export function flatten(object: Object, separator: string = '.'): Object {
const isValidObject = (value: {}): boolean => {
if (!value) {
return false;
}
const isArray = Array.isArray(value);
const isBuffer = Buffer.isBuffer(value);
const isΟbject = Object.prototype.toString.call(value) === '[object Object]';
const hasKeys = !!Object.keys(value).length;
return !isArray && !isBuffer && isΟbject && hasKeys;
};
const walker = (child: {}, path: Array<string> = []): Object => {
return Object.assign({}, ...Object.keys(child).map(key => isValidObject(child[key])
? walker(child[key], path.concat([key]))
: { [path.concat([key]).join(separator)]: child[key] })
);
};
return Object.assign({}, walker(object));
} |
This comment has been minimized.
This comment has been minimized.
ES6 version of @hellodeibu, had to remove the isBuffer check, since there's no Buffer class in ES6. function flatten(object, separator = '.') {
const isValidObject = value => {
if (!value) {
return false
}
const isArray = Array.isArray(value)
const isObject = Object.prototype.toString.call(value) === '[object Object]'
const hasKeys = !!Object.keys(value).length
return !isArray && isObject && hasKeys
}
const walker = (child, path = []) => {
return Object.assign({}, ...Object.keys(child).map(key => isValidObject(child[key])
? walker(child[key], path.concat([key]))
: { [path.concat([key]).join(separator)] : child[key] })
)
}
return Object.assign({}, walker(object))
} |
This comment has been minimized.
This comment has been minimized.
thanks @tonioriol it works great. |
This comment has been minimized.
This comment has been minimized.
@danieldietrich I like your version the most, but |
This comment has been minimized.
This comment has been minimized.
From this post I'm creates little utils |
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
const flattenObj = (obj) => {
if (!obj) {
throw Error(`flattenObj function expects an Object, received ${typeof obj}`)
}
return Object.keys(obj).reduce((acc, curr) => {
const objValue = obj[curr]
const ret = (objValue && objValue instanceof Object)
? flattenObj(objValue)
: { [curr]: objValue }
return Object.assign(acc, ret)
}, {})
} |
This comment has been minimized.
This comment has been minimized.
@pablohpsilva – your code is fundamentally wrong. Object flattening should accumulate keys: console.log(flattenObj({
users: {
set: "whatnot",
},
projects: {
set: "whatnot",
}
})) has to produce:
Otherwise it's pretty worthless (not to mention name clashes...). Also flattening must not flatten dates, regexps, arrays, etc. |
This comment has been minimized.
This comment has been minimized.
Enjoy: let isPlainObj = (o) => Boolean(
o && o.constructor && o.constructor.prototype && o.constructor.prototype.hasOwnProperty("isPrototypeOf")
)
let flattenObj = (obj, keys=[]) => {
return Object.keys(obj).reduce((acc, key) => {
return Object.assign(acc, isPlainObj(obj[key])
? flattenObj(obj[key], keys.concat(key))
: {[keys.concat(key).join(".")]: obj[key]}
)
}, {})
}
|
This comment has been minimized.
This comment has been minimized.
this version will return a flattened object which can be used to generate rails style params which will be interpreted to a ruby object. example output goes {a: {b: 'b', x: { y: 'y'}}, c: 'c', d: { e: 'e'}} => { 'a[b]': 'b', 'a[x][y]': 'y', 'c': 'c', 'd[e]': 'e' }. this can be useful if you would like to use rails as an api server from javascript / ajax.
|
This comment has been minimized.
This comment has been minimized.
TypeScript version with custom keys: type KeyFactory = (previousKey: string, currentKey: string) => string;
function flattenObject(ob: { [key: string]: any }, keyFactory: KeyFactory | null = null): { [key: string]: any } {
if (keyFactory === null) {
keyFactory = (previousKey, currentKey) => previousKey + '.' + currentKey;
}
const toReturn: { [key: string]: string } = {};
for (const i in ob) {
if (!ob.hasOwnProperty(i)) {
continue;
}
if ((typeof ob[i]) === 'object') {
const flatObject = flattenObject(ob[i]);
for (const x in flatObject) {
if (!flatObject.hasOwnProperty(x)) {
continue;
}
toReturn[keyFactory(i, x)] = flatObject[x];
}
} else {
toReturn[i] = ob[i];
}
}
return toReturn;
} Usage: const obj = { foo: { bar: 'baz' } };
flattenObject(obj, (previous, current) => `${previous}[${current}]`)
// Returns { 'foo[bar]': 'baz' } |
This comment has been minimized.
This comment has been minimized.
What is the lasted version? |
This comment has been minimized.
This comment has been minimized.
last version
Usage
|
This comment has been minimized.
This comment has been minimized.
in node version 9.x i got the error:
|
This comment has been minimized.
This comment has been minimized.
@tonioriol and others any body knows how to transform back the flattened object to the original object. Lets say i change the values of the 'x.y.z' keys in the flattened object, now i want to get the original object structure with the new values in them. |
This comment has been minimized.
This comment has been minimized.
I used @ruisebastiao and fixed few minor things
|
This comment has been minimized.
This comment has been minimized.
I used @arnigudj and added nested array flattening:
|
This comment has been minimized.
This comment has been minimized.
Thank you! |
This comment has been minimized.
This comment has been minimized.
anyone can revert this, i want to create object from single-depth object |
This comment has been minimized.
This comment has been minimized.
I used @pozylon and added a formatter function, which will be applied on each key. const flatten = (objectOrArray, prefix = '', formatter = (k) => ('.' + k)) => {
const nestElement = (prev, value, key) => (
(value && typeof value === 'object')
? { ...prev, ...flatten(value, `${prefix}${formatter(key)}`, formatter) }
: { ...prev, ...{ [`${prefix}${formatter(key)}`]: value } });
return Array.isArray(objectOrArray)
? objectOrArray.reduce(nestElement, {})
: Object.keys(objectOrArray).reduce(
(prev, element) => nestElement(prev, objectOrArray[element], element),
{},
);
}; |
This comment has been minimized.
This comment has been minimized.
Same thing as @mehrjoo, simply allows the possibility to have different formatter for the level 0 keys of your object const flatten = (objectOrArray, prefix = '', formatter = (k) => (k)) => {
const nestedFormatter = (k) => ('.' + k)
const nestElement = (prev, value, key) => (
(value && typeof value === 'object')
? { ...prev, ...flatten(value, `${prefix}${formatter(key)}`, nestedFormatter) }
: { ...prev, ...{ [`${prefix}${formatter(key)}`]: value } });
return Array.isArray(objectOrArray)
? objectOrArray.reduce(nestElement, {})
: Object.keys(objectOrArray).reduce(
(prev, element) => nestElement(prev, objectOrArray[element], element),
{},
);
}; This way for : You have this result : While with @mehrjoo you have this : |
This comment has been minimized.
This comment has been minimized.
very nicely done! Thank you for this! |
This comment has been minimized.
This comment has been minimized.
@edwardEvans094 I found code to reverse this at https://stackoverflow.com/questions/42694980/how-to-unflatten-a-javascript-object-in-a-daisy-chain-dot-notation-into-an-objec |
This comment has been minimized.
This comment has been minimized.
Really useful, thank you! |
This comment has been minimized.
This comment has been minimized.
anybody has an opposite function? to unflatten it? |
This comment has been minimized.
This comment has been minimized.
A Typescript 3.7+ version: export function flatten<T extends Record<string, any>>(
object: T,
path: string | null = null,
separator = '.'
): T {
return Object.keys(object).reduce((acc: T, key: string): T => {
const newPath = [path, key].filter(Boolean).join(separator);
return typeof object?.[key] === 'object'
? { ...acc, ...flatten(object[key], newPath, separator) }
: { ...acc, [newPath]: object[key] };
}, {} as T);
} |
This comment has been minimized.
This comment has been minimized.
@guillim
Results in: I'd like: |
This comment has been minimized.
This comment has been minimized.
What an awesome thread. Thanks to everyone here! I made an improvement from @x47188 version to handle export function flatten<T extends Record<string, any>>(
object: T,
path: string | null = null,
separator = '.'
): T {
return Object.keys(object).reduce((acc: T, key: string): T => {
const value = object[key];
const newPath = [path, key].filter(Boolean).join(separator);
const isObject = [
typeof value === 'object',
value !== null,
!(value instanceof Date),
!(value instanceof RegExp),
!(Array.isArray(value) && value.length === 0),
].every(Boolean);
return isObject
? { ...acc, ...flatten(value, newPath, separator) }
: { ...acc, [newPath]: value };
}, {} as T);
} |
This comment has been minimized.
This comment has been minimized.
I'm wondering how to do it similarly to what @gitty-git-git asks.
Anyone has an idea? @gitty-git-git did you find a way to do yours? |
This comment has been minimized.
This comment has been minimized.
slighlty modified from @codeBelt to output array indices as export function flatten<T extends Record<string, any>>(object: T, path: string | null = null, separator = '.'): T {
return Object.keys(object).reduce((acc: T, key: string): T => {
const value = object[key];
const newPath = Array.isArray(object)
? `${path ? path : ''}[${key}]`
: [path, key].filter(Boolean).join(separator);
const isObject = [
typeof value === 'object',
value !== null,
!(value instanceof Date),
!(value instanceof RegExp),
!(Array.isArray(value) && value.length === 0),
].every(Boolean);
return isObject
? { ...acc, ...flatten(value, newPath, separator) }
: { ...acc, [newPath]: value };
}, {} as T);
} Forked example: https://stackblitz.com/edit/typescript-pwsl83 |
This comment has been minimized.
This comment has been minimized.
I have put together a simple module, Flatify-obj based on this original gist with some additional tweaks and tests. Usage const flattenObject = require('flatify-obj');
flattenObject({foo: {bar: {unicorn: '🦄'}}})
//=> { 'foo.bar.unicorn': '🦄' }
flattenObject({foo: {unicorn: '🦄'}, bar: 'unicorn'}, {onlyLeaves: true});
//=> {unicorn: '🦄', bar: 'unicorn'}
|
This comment has been minimized.
This comment has been minimized.
Another one written in TypeScript: tree-to-flat-map. |
This comment has been minimized.
This comment has been minimized.
you are the best! |
This comment has been minimized.
This comment has been minimized.
@danzelbel like a charm! |
This comment has been minimized.
This comment has been minimized.
Jewel! |
This comment has been minimized.
Useful, thanks!