159 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			159 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
module.exports = flatten
 | 
						|
flatten.flatten = flatten
 | 
						|
flatten.unflatten = unflatten
 | 
						|
 | 
						|
function isBuffer (obj) {
 | 
						|
  return obj &&
 | 
						|
    obj.constructor &&
 | 
						|
    (typeof obj.constructor.isBuffer === 'function') &&
 | 
						|
    obj.constructor.isBuffer(obj)
 | 
						|
}
 | 
						|
 | 
						|
function keyIdentity (key) {
 | 
						|
  return key
 | 
						|
}
 | 
						|
 | 
						|
function flatten (target, opts) {
 | 
						|
  opts = opts || {}
 | 
						|
 | 
						|
  const delimiter = opts.delimiter || '.'
 | 
						|
  const maxDepth = opts.maxDepth
 | 
						|
  const transformKey = opts.transformKey || keyIdentity
 | 
						|
  const output = {}
 | 
						|
 | 
						|
  function step (object, prev, currentDepth) {
 | 
						|
    currentDepth = currentDepth || 1
 | 
						|
    Object.keys(object).forEach(function (key) {
 | 
						|
      const value = object[key]
 | 
						|
      const isarray = opts.safe && Array.isArray(value)
 | 
						|
      const type = Object.prototype.toString.call(value)
 | 
						|
      const isbuffer = isBuffer(value)
 | 
						|
      const isobject = (
 | 
						|
        type === '[object Object]' ||
 | 
						|
        type === '[object Array]'
 | 
						|
      )
 | 
						|
 | 
						|
      const newKey = prev
 | 
						|
        ? prev + delimiter + transformKey(key)
 | 
						|
        : transformKey(key)
 | 
						|
 | 
						|
      if (!isarray && !isbuffer && isobject && Object.keys(value).length &&
 | 
						|
        (!opts.maxDepth || currentDepth < maxDepth)) {
 | 
						|
        return step(value, newKey, currentDepth + 1)
 | 
						|
      }
 | 
						|
 | 
						|
      output[newKey] = value
 | 
						|
    })
 | 
						|
  }
 | 
						|
 | 
						|
  step(target)
 | 
						|
 | 
						|
  return output
 | 
						|
}
 | 
						|
 | 
						|
function unflatten (target, opts) {
 | 
						|
  opts = opts || {}
 | 
						|
 | 
						|
  const delimiter = opts.delimiter || '.'
 | 
						|
  const overwrite = opts.overwrite || false
 | 
						|
  const transformKey = opts.transformKey || keyIdentity
 | 
						|
  const result = {}
 | 
						|
 | 
						|
  const isbuffer = isBuffer(target)
 | 
						|
  if (isbuffer || Object.prototype.toString.call(target) !== '[object Object]') {
 | 
						|
    return target
 | 
						|
  }
 | 
						|
 | 
						|
  // safely ensure that the key is
 | 
						|
  // an integer.
 | 
						|
  function getkey (key) {
 | 
						|
    const parsedKey = Number(key)
 | 
						|
 | 
						|
    return (
 | 
						|
      isNaN(parsedKey) ||
 | 
						|
      key.indexOf('.') !== -1 ||
 | 
						|
      opts.object
 | 
						|
    ) ? key
 | 
						|
      : parsedKey
 | 
						|
  }
 | 
						|
 | 
						|
  function addKeys (keyPrefix, recipient, target) {
 | 
						|
    return Object.keys(target).reduce(function (result, key) {
 | 
						|
      result[keyPrefix + delimiter + key] = target[key]
 | 
						|
 | 
						|
      return result
 | 
						|
    }, recipient)
 | 
						|
  }
 | 
						|
 | 
						|
  function isEmpty (val) {
 | 
						|
    const type = Object.prototype.toString.call(val)
 | 
						|
    const isArray = type === '[object Array]'
 | 
						|
    const isObject = type === '[object Object]'
 | 
						|
 | 
						|
    if (!val) {
 | 
						|
      return true
 | 
						|
    } else if (isArray) {
 | 
						|
      return !val.length
 | 
						|
    } else if (isObject) {
 | 
						|
      return !Object.keys(val).length
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  target = Object.keys(target).reduce(function (result, key) {
 | 
						|
    const type = Object.prototype.toString.call(target[key])
 | 
						|
    const isObject = (type === '[object Object]' || type === '[object Array]')
 | 
						|
    if (!isObject || isEmpty(target[key])) {
 | 
						|
      result[key] = target[key]
 | 
						|
      return result
 | 
						|
    } else {
 | 
						|
      return addKeys(
 | 
						|
        key,
 | 
						|
        result,
 | 
						|
        flatten(target[key], opts)
 | 
						|
      )
 | 
						|
    }
 | 
						|
  }, {})
 | 
						|
 | 
						|
  Object.keys(target).forEach(function (key) {
 | 
						|
    const split = key.split(delimiter).map(transformKey)
 | 
						|
    let key1 = getkey(split.shift())
 | 
						|
    let key2 = getkey(split[0])
 | 
						|
    let recipient = result
 | 
						|
 | 
						|
    while (key2 !== undefined) {
 | 
						|
      if (key1 === '__proto__') {
 | 
						|
        return
 | 
						|
      }
 | 
						|
 | 
						|
      const type = Object.prototype.toString.call(recipient[key1])
 | 
						|
      const isobject = (
 | 
						|
        type === '[object Object]' ||
 | 
						|
        type === '[object Array]'
 | 
						|
      )
 | 
						|
 | 
						|
      // do not write over falsey, non-undefined values if overwrite is false
 | 
						|
      if (!overwrite && !isobject && typeof recipient[key1] !== 'undefined') {
 | 
						|
        return
 | 
						|
      }
 | 
						|
 | 
						|
      if ((overwrite && !isobject) || (!overwrite && recipient[key1] == null)) {
 | 
						|
        recipient[key1] = (
 | 
						|
          typeof key2 === 'number' &&
 | 
						|
          !opts.object ? [] : {}
 | 
						|
        )
 | 
						|
      }
 | 
						|
 | 
						|
      recipient = recipient[key1]
 | 
						|
      if (split.length > 0) {
 | 
						|
        key1 = getkey(split.shift())
 | 
						|
        key2 = getkey(split[0])
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // unflatten again for 'messy objects'
 | 
						|
    recipient[key1] = unflatten(target[key], opts)
 | 
						|
  })
 | 
						|
 | 
						|
  return result
 | 
						|
}
 |