Skip to content

Instantly share code, notes, and snippets.

@penguinboy
Created January 2, 2011 01:55
Show Gist options
  • Save penguinboy/762197 to your computer and use it in GitHub Desktop.
Save penguinboy/762197 to your computer and use it in GitHub Desktop.
Flatten javascript objects into a single-depth object
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;
};
@jgcoding
Copy link

very nicely done! Thank you for this!

@paheld
Copy link

paheld commented Jan 29, 2019

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

@jaumzors
Copy link

Really useful, thank you!

@aronmgv
Copy link

aronmgv commented Mar 15, 2019

anybody has an opposite function? to unflatten it?

@x47188
Copy link

x47188 commented Jan 20, 2020

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);
}

@gitty-git-git
Copy link

gitty-git-git commented Mar 21, 2020

@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"]

@codeBelt
Copy link

codeBelt commented Jul 26, 2020

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);
}

Example: https://stackblitz.com/edit/typescript-shdjoq

@lveillard
Copy link

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?

@danzelbel
Copy link

danzelbel commented Sep 10, 2020

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

@rocktimsaikia
Copy link

rocktimsaikia commented Oct 5, 2020

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 🦄

@tibdex
Copy link

tibdex commented Nov 17, 2020

Another one written in TypeScript: tree-to-flat-map.

@a201150209
Copy link

you are the best!

@oshliaer
Copy link

@danzelbel like a charm!

@ludob78
Copy link

ludob78 commented Dec 7, 2020

Jewel!

@wowjeeez
Copy link

wowjeeez commented May 22, 2021

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
}

@jcv-pt
Copy link

jcv-pt commented Aug 26, 2021

Thank you for the original implementation!

@KuSh
Copy link

KuSh commented Feb 9, 2022

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 };
  }, {});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment