generated from sapphiredev/sapphire-template
-
-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(utilities): add getDeepObjectKeys (#413)
* feat(utilities): add getDeepObjectKeys * chore: here be dragons permalink: http://whatthecommit.com/29d3421ded23e53d3882f6fb6aa758e3 * chore: continued development... permalink: http://whatthecommit.com/d5a41f912fb8d73730cc52801ca6ab92
- Loading branch information
Showing
3 changed files
with
78 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { isObject } from './isObject'; | ||
import type { AnyObject } from './utilityTypes'; | ||
|
||
/** | ||
* Flattens an object to a list of its keys, traversing deeply into nested objects and arrays of objects. | ||
* | ||
* @note By default Nested array values are flattened to `arrayKey.${index}.subKey`. This can be changed to `arrayKey[${index}].subKey` by setting `arrayKeysBracedIndex` to `true`. | ||
* | ||
* @param obj The object of which to deeply retrieve its keys | ||
* @returns An array of strings holding the keys of the object | ||
*/ | ||
export function getDeepObjectKeys<T>( | ||
obj: AnyObject<T>, | ||
{ arrayKeysIndexStyle = 'dotted' }: GetDeepObjectKeysOptions = { arrayKeysIndexStyle: 'dotted' } | ||
): string[] { | ||
const keys: string[] = []; | ||
|
||
for (const [key, value] of Object.entries(obj)) { | ||
if (Array.isArray(value)) { | ||
for (const [index, innerValue] of value.entries()) { | ||
const arraySubKeys = getDeepObjectKeys(innerValue); | ||
keys.push( | ||
...arraySubKeys.map((arraySubKey) => { | ||
switch (arrayKeysIndexStyle) { | ||
case 'braces-with-dot': | ||
return `${key}[${index}].${arraySubKey}`; | ||
case 'braces': | ||
return `${key}[${index}]${arraySubKey}`; | ||
case 'dotted': | ||
default: | ||
return `${key}.${index}.${arraySubKey}`; | ||
} | ||
}) | ||
); | ||
} | ||
} else if (isObject(value)) { | ||
const objectSubKeys = getDeepObjectKeys(value); | ||
keys.push(...objectSubKeys.map((subKey) => `${key}.${subKey}`)); | ||
} else { | ||
keys.push(key); | ||
} | ||
} | ||
|
||
return keys; | ||
} | ||
|
||
/** | ||
* The options for {@link getDeepObjectKeys} | ||
*/ | ||
export interface GetDeepObjectKeysOptions { | ||
/** | ||
* Whether to use `.${index}.` (`'dotted'`), `[${index}].`, (`'braces-with-dot'`) or `[${index}]` (`'braces'`) to separate array keys | ||
* @default 'dotted' | ||
*/ | ||
arrayKeysIndexStyle?: 'dotted' | 'braces-with-dot' | 'braces'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { getDeepObjectKeys, GetDeepObjectKeysOptions, NonNullObject } from '../src'; | ||
|
||
describe('getDeepObjectKeys', () => { | ||
const scenarios: Scenario[] = [ | ||
[{ a: 1, b: 2 }, {}, ['a', 'b']], | ||
[{ a: [[]] }, {}, []], | ||
[{ a: [[{ b: 0 }]] }, {}, ['a.0.0.b']], | ||
[{ a: { b: 1, c: 2 }, d: 3 }, {}, ['a.b', 'a.c', 'd']], | ||
[{ a: [{ b: 1, c: 2 }, { d: 3 }] }, {}, ['a.0.b', 'a.0.c', 'a.1.d']], | ||
[{ a: [{ b: 1, c: 2 }, { d: 3 }] }, { arrayKeysIndexStyle: 'braces' }, ['a[0]b', 'a[0]c', 'a[1]d']], | ||
[{ a: [{ b: 1, c: 2 }, { d: 3 }] }, { arrayKeysIndexStyle: 'braces-with-dot' }, ['a[0].b', 'a[0].c', 'a[1].d']], | ||
[{ a: [{ b: 1, c: 2 }, { d: 3 }] }, { arrayKeysIndexStyle: 'dotted' }, ['a.0.b', 'a.0.c', 'a.1.d']], | ||
[{ a: [{ b: 1, c: 2 }, { d: 3 }], e: 4, f: { g: { h: { i: { j: { k: 5 } } } } } }, {}, ['a.0.b', 'a.0.c', 'a.1.d', 'e', 'f.g.h.i.j.k']] | ||
]; | ||
|
||
test.each(scenarios)('GIVEN %j with %j THEN keys should equal %j', (inputObj, options, outputStrings) => { | ||
expect(getDeepObjectKeys(inputObj, options)).toEqual(outputStrings); | ||
}); | ||
}); | ||
|
||
type Scenario = [NonNullObject, GetDeepObjectKeysOptions, string[]]; |