Dismiss
  • Toggle Theme
  • View as Mobile

Flatten & unflatten javascript objects by path

Convert a nested object into a flat structure. A.k.a Flatten an object by its keys. This is handy for fast object lookups by value path in certain scenarios.

// Convert a nested object
{
  here: {
    is: 'my',
    object: 'with',
    stuff: 'in it'
  },
  containing: {
    many: ['things', 'inside'],
  }
}

// Into a flat structure
{
  here.is: "my"
  here.object: "with"
  here.stuff: "in it"
  containing.many[0]: "things"
  containing.many[1]: "inside"
}

Here is the code:

function flatten(data) {
  const 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++) { // eslint-disable-line
        recurse(cur[i], `${prop}[${i}]`)
      }
      if (l == 0) { // eslint-disable-line
        result[prop] = []
      }
    } else {
      let isEmpty = true
      for (const p in cur) { // eslint-disable-line
        isEmpty = false
        recurse(cur[p], prop ? `${prop}.${p}` : p)
      }
      if (isEmpty && prop) {
        result[prop] = {}
      }
    }
  }
  recurse(data, '')
  return result
}

function unflatten(data) {
  if (Object(data) !== data || Array.isArray(data)) {
    return data
  }
  let regex = /\.?([^.\[\]]+)|\[(\d+)\]/g
  let resultholder = {}
  for (const p in data) {
    let cur = resultholder
    let prop = ''
    let m
    while (m = regex.exec(p)) { // eslint-disable-line
      // console.log('m[1]', m[1])
      // console.log('m[2]', m[2])
      // cur = cur[prop] || (cur[prop] = ((m[2] || !isNaN(m[1])) ? [] : {}))
      cur = cur[prop] || (cur[prop] = (m[2] ? [] : {}))
      prop = m[2] || m[1]
      // remove placeholder
      prop = prop.replace(/XPERIOD/g, '.')
    }
    cur[prop] = data[p]
  }
  return resultholder[''] || resultholder
}


var object = {
 here: {
   is: 'my',
   object: 'with',
   stuff: 'in it'
 },
 containing: {
  many: ['things', 'inside'],
 }
}

var flat = flatten(object)
console.log('flat', flat)


var returnToObject = unflatten(flat)
console.log('returnToObject', returnToObject)

Let me know if you have a better way 😃