Skip to content

Instantly share code, notes, and snippets.

@penguinboy
Created January 2, 2011 01:55
Show Gist options
  • Select an option

  • Save penguinboy/762197 to your computer and use it in GitHub Desktop.

Select an option

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;
};
@paheld

paheld commented Jan 29, 2019

Copy link
Copy Markdown

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

@jaumplopes

Copy link
Copy Markdown

Really useful, thank you!

@aronmgv

aronmgv commented Mar 15, 2019

Copy link
Copy Markdown

anybody has an opposite function? to unflatten it?

ghost commented Jan 20, 2020

Copy link
Copy Markdown

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

gitty-git-git commented Mar 21, 2020

Copy link
Copy Markdown

@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

codeBelt commented Jul 26, 2020

Copy link
Copy Markdown

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
Copy Markdown

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

danzelbel commented Sep 10, 2020

Copy link
Copy Markdown

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

rocktimsaikia commented Oct 5, 2020

Copy link
Copy Markdown

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

tibdex commented Nov 17, 2020

Copy link
Copy Markdown

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

@a201150209

Copy link
Copy Markdown

you are the best!

@oshliaer

Copy link
Copy Markdown

@danzelbel like a charm!

@ludob78

ludob78 commented Dec 7, 2020

Copy link
Copy Markdown

Jewel!

@wowjeeez

wowjeeez commented May 22, 2021

Copy link
Copy Markdown

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

jcv-pt commented Aug 26, 2021

Copy link
Copy Markdown

Thank you for the original implementation!

@KuSh

KuSh commented Feb 9, 2022

Copy link
Copy Markdown

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