-
Notifications
You must be signed in to change notification settings - Fork 196
/
index.js
157 lines (130 loc) · 3.84 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
function isBuffer (obj) {
return obj &&
obj.constructor &&
(typeof obj.constructor.isBuffer === 'function') &&
obj.constructor.isBuffer(obj)
}
function keyIdentity (key) {
return key
}
export 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
}
export 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
}