-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathflatObject.ts
More file actions
100 lines (93 loc) · 3.15 KB
/
flatObject.ts
File metadata and controls
100 lines (93 loc) · 3.15 KB
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
import type { Constructor, TypedArray } from "https://lib.deno.dev/x/ayonli_jsext@latest/index.ts";
import { isDictLike, isArrayLike, isBufferLike } from "https://lib.deno.dev/x/is_like@latest/index.js";
import isVoid from "./isVoid.ts";
type OmitChildrenNodes<T> = Pick<T, {
[K in keyof T]: T[K] extends TypedArray
? K
: T[K] extends (any[] | ArrayLike<any> | Function | Constructor<any> | Map<any, any> | Set<any> | Promise<any> | TypedArray)
? K
: T[K] extends object
? never
: K;
}[keyof T]>;
type OmitChildrenElements<T> = Pick<T, {
[K in keyof T]: T[K] extends TypedArray
? K
: T[K] extends (Function | Constructor<any> | Map<any, any> | Set<any> | Promise<any> | TypedArray)
? K
: T[K] extends (object | any[] | ArrayLike<any>)
? never
: K;
}[keyof T]>;
/**
* Create an object with flatted properties of the original object, the children
* nodes' properties will be transformed to a string-represented path.
* NOTE: this function also flat array/array-like nodes (except for TypedArray).
* @param depth Default value: `1`.
* @example
* flatObject({ foo: { bar: "Hello, World!" } }) === { "foo.bar": "Hello, World!" }
*/
export default function flatObject<T extends object>(
obj: T,
depth?: number
): OmitChildrenNodes<T> & Record<string | symbol, any>;
export default function flatObject<T extends object>(
obj: T,
depth: number,
flatArray: true
): OmitChildrenElements<T> & Record<string | symbol, any>;
export default function flatObject(obj: any, depth = 1, flatArray = false) {
return flatDeep({}, obj, "", 0, depth, flatArray);
}
function flatDeep(
carrier: any,
source: any,
field: string,
depth: number,
maxDepth: number,
flatArray: boolean
) {
let isArr: boolean | undefined;
let isDict: boolean | undefined;
let isContent = !isVoid(field) && field !== "";
if (depth === maxDepth || (
!(isArr = isArrayLike(source, true) && !isBufferLike(source)) &&
!(isDict = isDictLike(source))
)) {
carrier[field] = source;
} else if (isDict) {
Reflect.ownKeys(<object>source).forEach(key => {
let value = (<any>source)[key];
if (typeof key === "symbol") {
if (depth === 0) { // only allow top-level symbol properties
carrier[key] = value;
}
} else {
flatDeep(
carrier,
value,
isContent ? `${field}.${key}` : key,
isContent ? depth + 1 : depth,
maxDepth,
flatArray
);
}
});
} else if (isArr) {
if (flatArray) {
for (let i = 0, len = (<any[]>source).length; i < len; ++i) {
flatDeep(
carrier,
(<any[]>source)[i],
isContent ? `${field}.${i}` : String(i),
isContent ? depth + 1 : depth,
maxDepth,
flatArray
);
}
} else {
carrier[field] = source;
}
}
return carrier;
}