index.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. module.exports = flatten
  2. flatten.flatten = flatten
  3. flatten.unflatten = unflatten
  4. function isBuffer (obj) {
  5. return obj &&
  6. obj.constructor &&
  7. (typeof obj.constructor.isBuffer === 'function') &&
  8. obj.constructor.isBuffer(obj)
  9. }
  10. function keyIdentity (key) {
  11. return key
  12. }
  13. function flatten (target, opts) {
  14. opts = opts || {}
  15. const delimiter = opts.delimiter || '.'
  16. const maxDepth = opts.maxDepth
  17. const transformKey = opts.transformKey || keyIdentity
  18. const output = {}
  19. function step (object, prev, currentDepth) {
  20. currentDepth = currentDepth || 1
  21. Object.keys(object).forEach(function (key) {
  22. const value = object[key]
  23. const isarray = opts.safe && Array.isArray(value)
  24. const type = Object.prototype.toString.call(value)
  25. const isbuffer = isBuffer(value)
  26. const isobject = (
  27. type === '[object Object]' ||
  28. type === '[object Array]'
  29. )
  30. const newKey = prev
  31. ? prev + delimiter + transformKey(key)
  32. : transformKey(key)
  33. if (!isarray && !isbuffer && isobject && Object.keys(value).length &&
  34. (!opts.maxDepth || currentDepth < maxDepth)) {
  35. return step(value, newKey, currentDepth + 1)
  36. }
  37. output[newKey] = value
  38. })
  39. }
  40. step(target)
  41. return output
  42. }
  43. function unflatten (target, opts) {
  44. opts = opts || {}
  45. const delimiter = opts.delimiter || '.'
  46. const overwrite = opts.overwrite || false
  47. const transformKey = opts.transformKey || keyIdentity
  48. const result = {}
  49. const isbuffer = isBuffer(target)
  50. if (isbuffer || Object.prototype.toString.call(target) !== '[object Object]') {
  51. return target
  52. }
  53. // safely ensure that the key is
  54. // an integer.
  55. function getkey (key) {
  56. const parsedKey = Number(key)
  57. return (
  58. isNaN(parsedKey) ||
  59. key.indexOf('.') !== -1 ||
  60. opts.object
  61. ) ? key
  62. : parsedKey
  63. }
  64. function addKeys (keyPrefix, recipient, target) {
  65. return Object.keys(target).reduce(function (result, key) {
  66. result[keyPrefix + delimiter + key] = target[key]
  67. return result
  68. }, recipient)
  69. }
  70. function isEmpty (val) {
  71. const type = Object.prototype.toString.call(val)
  72. const isArray = type === '[object Array]'
  73. const isObject = type === '[object Object]'
  74. if (!val) {
  75. return true
  76. } else if (isArray) {
  77. return !val.length
  78. } else if (isObject) {
  79. return !Object.keys(val).length
  80. }
  81. }
  82. target = Object.keys(target).reduce(function (result, key) {
  83. const type = Object.prototype.toString.call(target[key])
  84. const isObject = (type === '[object Object]' || type === '[object Array]')
  85. if (!isObject || isEmpty(target[key])) {
  86. result[key] = target[key]
  87. return result
  88. } else {
  89. return addKeys(
  90. key,
  91. result,
  92. flatten(target[key], opts)
  93. )
  94. }
  95. }, {})
  96. Object.keys(target).forEach(function (key) {
  97. const split = key.split(delimiter).map(transformKey)
  98. let key1 = getkey(split.shift())
  99. let key2 = getkey(split[0])
  100. let recipient = result
  101. while (key2 !== undefined) {
  102. if (key1 === '__proto__') {
  103. return
  104. }
  105. const type = Object.prototype.toString.call(recipient[key1])
  106. const isobject = (
  107. type === '[object Object]' ||
  108. type === '[object Array]'
  109. )
  110. // do not write over falsey, non-undefined values if overwrite is false
  111. if (!overwrite && !isobject && typeof recipient[key1] !== 'undefined') {
  112. return
  113. }
  114. if ((overwrite && !isobject) || (!overwrite && recipient[key1] == null)) {
  115. recipient[key1] = (
  116. typeof key2 === 'number' &&
  117. !opts.object ? [] : {}
  118. )
  119. }
  120. recipient = recipient[key1]
  121. if (split.length > 0) {
  122. key1 = getkey(split.shift())
  123. key2 = getkey(split[0])
  124. }
  125. }
  126. // unflatten again for 'messy objects'
  127. recipient[key1] = unflatten(target[key], opts)
  128. })
  129. return result
  130. }