UNPKG

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