Skip to content

Commit 79ced64

Browse files
committed
refactor: improve flatten function
- uses grok llm, so could be further issues yet
1 parent b422004 commit 79ced64

File tree

1 file changed

+50
-24
lines changed

1 file changed

+50
-24
lines changed

src/util/flatten.ts

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,43 +13,69 @@ type Flattened<T> = T extends Array<infer U>
1313
? ExcludeFunctionsAndPrivate<T>
1414
: T
1515

16-
function flatten<T>(obj: T): Flattened<T> {
17-
// Handle arrays
18-
if (Array.isArray(obj)) {
19-
return obj.map((item) => flatten(item)) as Flattened<T>
20-
}
21-
16+
function flatten<T>(obj: T, ...props: Record<string, boolean | string>[]): Flattened<T> {
2217
// Handle null or non-objects
2318
if (!obj || typeof obj !== 'object') {
2419
return obj as Flattened<T>
2520
}
2621

27-
// Handle Date objects
28-
if (obj instanceof Date) {
29-
return obj.toISOString() as Flattened<T>
22+
// Handle arrays
23+
if (Array.isArray(obj)) {
24+
return obj.map((item) => flatten(item)) as Flattened<T>
3025
}
3126

32-
const result: Record<string, unknown> = {}
27+
// Get all non-private properties (exclude keys starting with '_')
28+
const objProps = Object.keys(obj)
29+
.filter((key) => !key.startsWith('_'))
30+
.map((key) => ({ [key]: true }))
3331

34-
for (const key in obj) {
35-
// biome-ignore lint/suspicious/noExplicitAny: Safe
36-
if (key.startsWith('_') || typeof (obj as any)[key] === 'function') {
37-
continue
38-
}
32+
// Merge provided props with object props safely
33+
const mergedProps: Record<string, boolean | string> = Object.assign(
34+
{},
35+
...objProps, // Spread array of objects
36+
...props, // Spread additional props
37+
)
38+
39+
const out: Record<string, unknown> = {}
40+
41+
for (const [prop, newProp] of Object.entries(mergedProps)) {
42+
if (!newProp) continue
43+
const outputKey = newProp === true ? prop : newProp
3944

4045
// biome-ignore lint/suspicious/noExplicitAny: Safe
41-
const value = (obj as any)[key]
42-
43-
if (value && typeof value === 'object' && 'toJSON' in value) {
44-
result[key] = flatten(value)
45-
} else if (Array.isArray(value)) {
46-
result[key] = flatten(value)
47-
} else {
48-
result[key] = value
46+
const element = (obj as any)[prop]
47+
const elemIsObj = element && typeof element === 'object'
48+
// biome-ignore lint/suspicious/noShadowRestrictedNames: Safe
49+
const valueOf = elemIsObj && typeof element.valueOf === 'function' ? element.valueOf() : null
50+
const hasToJSON = elemIsObj && typeof element.toJSON === 'function'
51+
52+
// Handle Date objects explicitly
53+
if (element instanceof Date) {
54+
out[outputKey] = element.toISOString()
55+
}
56+
// If it's an array, process each element
57+
else if (Array.isArray(element)) {
58+
out[outputKey] = element.map((elm) => (hasToJSON ? elm.toJSON() : flatten(elm)))
59+
}
60+
// If it has a toJSON method, use its result
61+
else if (hasToJSON) {
62+
out[outputKey] = element.toJSON()
63+
}
64+
// If it's an object with a primitive valueOf, use that
65+
else if (elemIsObj && valueOf && typeof valueOf !== 'object') {
66+
out[outputKey] = valueOf
67+
}
68+
// If it's a non-primitive object, flatten it
69+
else if (elemIsObj) {
70+
out[outputKey] = flatten(element)
71+
}
72+
// Otherwise, use the element as-is (primitives)
73+
else {
74+
out[outputKey] = element
4975
}
5076
}
5177

52-
return result as Flattened<T>
78+
return out as Flattened<T>
5379
}
5480

5581
export { flatten, type Flattened }

0 commit comments

Comments
 (0)