-
-
Save penguinboy/762197 to your computer and use it in GitHub Desktop.
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; | |
}; |
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),
{},
);
};
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 :
input = { 'a':{ 'b':{ 'b2':2 }, 'c':{ 'c2':2, 'c3':3 } } }
You have this result :
{ a.b.b2: 2, a.c.c2: 2, a.c.c3: 3 }
While with @mehrjoo you have this :
{ .a.b.b2: 2, .a.c.c2: 2, .a.c.c3: 3 }
very nicely done! Thank you for this!
anyone can revert this, i want to create object from single-depth object
@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
Really useful, thank you!
anybody has an opposite function? to unflatten it?
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);
}
@guillim
How do I remove the numbers from the keys in an array of objects? I have this fiddle. This data:
var data = {
"ticker": "AAPL",
"name": "Apple",
"data": [
1,2,3,4
],
"obj": [
{"revenue": 1, "income": 11},
{"revenue": 2, "income": 22},
],
}
Results in:
["ticker", "name", "data.0", "data.1", "data.2", "data.3", "obj.0.revenue", "obj.0.income", "obj.1.revenue", "obj.1.income"]
I'd like:
["ticker", "name", "data", "data", "data", "data", "obj.revenue", "obj.income", "obj.revenue", "obj.income"]
What an awesome thread. Thanks to everyone here!
I made an improvement from @x47188 version to handle null
values and empty []
arrays. Also added the Date
and Regex
object for the hell of it.
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);
}
I'm wondering how to do it similarly to what @gitty-git-git asks.
But i want to keep arrays as they are unless they have objects, in that case flatten the objects.
For example this:
flatten({a:{b:2},c:[{e:{f:4}}]})
{
"a.b": 2,
"c": [
{
"e.f": 4
}
]
}
Anyone has an idea? @gitty-git-git did you find a way to do yours?
slighlty modified from @codeBelt to output array indices as [0]
instead of as property .0
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
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'}
For additional features PRs are welcome 🦄
Another one written in TypeScript: tree-to-flat-map.
you are the best!
@danzelbel like a charm!
Jewel!
This is my implementation, it just flattens every object in the parent object into one, without keying them with object.key
.
Working example
Code:
function flatten(obj = {}) {
const doneObject = {}
for (const [k, v] of Object.entries(obj)) {
if (typeof v == "object" && !(v instanceof Date) && !Array.isArray(v) && !(v instanceof regExp)) {
Object.assign(doneObject, flatten(v))
} else {
doneObject[k] = v
}
}
return doneObject
}
Thank you for the original implementation!
A version that leaves undefined behind, array as is and don't try to flatten primitive objects (Number, Boolean, BigInt and String essentially) :
const flatten = <T extends Record<string, any>>(object: T, path?: string): Record<string, any> =>
Object.entries(object).reduce((acc, [key, val]) => {
if (val === undefined) return acc;
if (path) key = `${path}.${key}`;
if (typeof val === 'object' && val !== null && !(val instanceof Date) && !(val instanceof RegExp) && !Array.isArray(val)) {
if (val !== val.valueOf()) {
return { ...acc, [key]: val.valueOf() };
}
return { ...acc, ...flatten(val, key) };
}
return { ...acc, [key]: val };
}, {});
anyone can revert this, i want to create object from single-depth object