111 lines
2.6 KiB
JavaScript
111 lines
2.6 KiB
JavaScript
|
var isBuffer = require('is-buffer')
|
||
|
|
||
|
module.exports = flatten
|
||
|
flatten.flatten = flatten
|
||
|
flatten.unflatten = unflatten
|
||
|
|
||
|
function flatten (target, opts) {
|
||
|
opts = opts || {}
|
||
|
|
||
|
var delimiter = opts.delimiter || '.'
|
||
|
var maxDepth = opts.maxDepth
|
||
|
var output = {}
|
||
|
|
||
|
function step (object, prev, currentDepth) {
|
||
|
currentDepth = currentDepth || 1
|
||
|
Object.keys(object).forEach(function (key) {
|
||
|
var value = object[key]
|
||
|
var isarray = opts.safe && Array.isArray(value)
|
||
|
var type = Object.prototype.toString.call(value)
|
||
|
var isbuffer = isBuffer(value)
|
||
|
var isobject = (
|
||
|
type === '[object Object]' ||
|
||
|
type === '[object Array]'
|
||
|
)
|
||
|
|
||
|
var newKey = prev
|
||
|
? prev + delimiter + key
|
||
|
: 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 || {}
|
||
|
|
||
|
var delimiter = opts.delimiter || '.'
|
||
|
var overwrite = opts.overwrite || false
|
||
|
var result = {}
|
||
|
|
||
|
var 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) {
|
||
|
var parsedKey = Number(key)
|
||
|
|
||
|
return (
|
||
|
isNaN(parsedKey) ||
|
||
|
key.indexOf('.') !== -1 ||
|
||
|
opts.object
|
||
|
) ? key
|
||
|
: parsedKey
|
||
|
}
|
||
|
|
||
|
var sortedKeys = Object.keys(target).sort(function (keyA, keyB) {
|
||
|
return keyA.length - keyB.length
|
||
|
})
|
||
|
|
||
|
sortedKeys.forEach(function (key) {
|
||
|
var split = key.split(delimiter)
|
||
|
var key1 = getkey(split.shift())
|
||
|
var key2 = getkey(split[0])
|
||
|
var recipient = result
|
||
|
|
||
|
while (key2 !== undefined) {
|
||
|
var type = Object.prototype.toString.call(recipient[key1])
|
||
|
var 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
|
||
|
}
|