From 0ebd4f5797fe74f0fa8ce3e99ace2f858deee161 Mon Sep 17 00:00:00 2001 From: mew-ton Date: Wed, 13 Sep 2023 18:57:44 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20key-prefix=20=E6=A9=9F=E8=83=BD?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=20(#5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: key-prefix 機能を追加 * Create empty-eagles-argue.md * Update README.md --- .changeset/empty-eagles-argue.md | 5 ++++ README.md | 13 +++++++-- src/fold.ts | 7 +++-- src/twist.ts | 4 +-- src/type.ts | 33 ++++++++++++++++++---- src/unfold.ts | 22 +++++++-------- test/fold.spec.ts | 47 ++++++++++++++++++++++++++++++++ 7 files changed, 108 insertions(+), 23 deletions(-) create mode 100644 .changeset/empty-eagles-argue.md diff --git a/.changeset/empty-eagles-argue.md b/.changeset/empty-eagles-argue.md new file mode 100644 index 0000000..7482038 --- /dev/null +++ b/.changeset/empty-eagles-argue.md @@ -0,0 +1,5 @@ +--- +"json-origami": minor +--- + +feat: key-prefix 機能を追加 diff --git a/README.md b/README.md index 7ba75a9..d48e2f0 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ console.log(result) ### arrayIndex **Type**: `'dot' | 'bracket'` -**Default**: 'bracket' +**Default**: `'bracket'` Determines the formatting of array indexes when keys are compressed. Choose between dot notation (.0) or bracket notation ([0]). @@ -106,10 +106,19 @@ conole.log(result) /// { a: 1, 'b.c': 2, 'b.d.0': 3, 'b.d.1.e': 4 } ``` +### keyPrefix + +Options for 'fold' + +**Type**: `string` +**Default**: `''` (empty string) + +Adds a specified prefix to the keys in the output. + ## License [MIT](./LICENSE) ## Contributing -see [CONTRIBUTING.md}(./CONTRIBUTING.md) +see [CONTRIBUTING.md](./CONTRIBUTING.md) diff --git a/src/fold.ts b/src/fold.ts index c0e72f8..939b821 100644 --- a/src/fold.ts +++ b/src/fold.ts @@ -1,5 +1,5 @@ import { - defaultFoldOption, + defaultCommonOption, type Dictionary, type FoldOption, type Folded, @@ -34,7 +34,10 @@ import { */ export function fold(obj: D, option?: FoldOption): Folded { return Object.fromEntries( - flatEntries('', obj, { ...defaultFoldOption, ...option } as FixedFoldOption) + flatEntries(option?.keyPrefix ?? '', obj, { + ...defaultCommonOption, + ...option + } as FixedFoldOption) ) as Folded } diff --git a/src/twist.ts b/src/twist.ts index e5d2c1b..9f537b5 100644 --- a/src/twist.ts +++ b/src/twist.ts @@ -1,5 +1,5 @@ import { fold, unfold } from '.' -import type { Dictionary, MoveMap, Twist, FoldOption } from './type' +import type { Dictionary, MoveMap, Twist, TwistOption } from './type' /** * @@ -9,7 +9,7 @@ import type { Dictionary, MoveMap, Twist, FoldOption } from './type' export function twist( obj: D, moveMap: M, - option?: FoldOption + option?: TwistOption ): Twist { const folded = fold(obj, option) diff --git a/src/type.ts b/src/type.ts index 12692ec..36982e5 100644 --- a/src/type.ts +++ b/src/type.ts @@ -40,18 +40,39 @@ export type Twist<_D extends Dictionary, _M extends MoveMap> = object */ export type ArrayIndex = 'dot' | 'bracket' +interface CommonOption { + /** + * @default 'bracet' + */ + arrayIndex?: 'dot' | 'bracket' +} + /** * */ -export interface FoldOption { +export interface FoldOption extends CommonOption { /** - * @default 'bracet' + * */ - arrayIndex?: 'dot' | 'bracket' + keyPrefix?: string } -export const defaultFoldOption = { - arrayIndex: 'bracket' +export const defaultCommonOption = { + arrayIndex: 'bracket' as ArrayIndex } satisfies FoldOption -export type FixedFoldOption = Readonly> +export type FixedFoldOption = Readonly + +/** + * + */ +export interface UnfoldOption extends CommonOption {} + +export type FixedUnfoldOption = Readonly + +/** + * + */ +export interface TwistOption extends CommonOption {} + +export type FixedTwistOption = Readonly diff --git a/src/unfold.ts b/src/unfold.ts index 6050be1..6e17896 100644 --- a/src/unfold.ts +++ b/src/unfold.ts @@ -1,9 +1,9 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { - defaultFoldOption, + defaultCommonOption, type ArrayIndex, - type FixedFoldOption, - type FoldOption, + type FixedUnfoldOption, + type UnfoldOption, type Folded, type Unfolded } from './type' @@ -32,11 +32,11 @@ import { * // } * ``` */ -export function unfold>(kv: KV, option?: FoldOption): Unfolded { +export function unfold>(kv: KV, option?: UnfoldOption): Unfolded { const fixedOpion = { - ...defaultFoldOption, + ...defaultCommonOption, ...option - } as FixedFoldOption + } as FixedUnfoldOption validateKeys(kv, fixedOpion) return unfoldInternal(Object.entries(kv), fixedOpion) as Unfolded @@ -47,7 +47,7 @@ const validateIndexMap = { bracket: (k) => /\[\d+\]/.test(k) } satisfies Record boolean> -function validateKeys(kv: Folded, opt: FixedFoldOption) { +function validateKeys(kv: Folded, opt: FixedUnfoldOption) { for (const key in kv) { if (/\d+/.test(key)) { validateNumberKey(key, opt) @@ -59,7 +59,7 @@ function validateKeys(kv: Folded, opt: FixedFoldOption) { } } -function validateNumberKey(key: string, { arrayIndex }: FixedFoldOption) { +function validateNumberKey(key: string, { arrayIndex }: FixedUnfoldOption) { if (!validateIndexMap[arrayIndex](key)) { throw new Error(`Invalid key ${key}`) } @@ -70,7 +70,7 @@ const extractHeadIndexMap = { bracket: (k) => (k.match(/^\[(\d+)\]/) ?? [])[1] } satisfies Record string | undefined> -function extractHeadKey(key: string, { arrayIndex }: FixedFoldOption): string | number { +function extractHeadKey(key: string, { arrayIndex }: FixedUnfoldOption): string | number { const indexHead = extractHeadIndexMap[arrayIndex](key) if (indexHead !== undefined) { @@ -83,7 +83,7 @@ function extractHeadKey(key: string, { arrayIndex }: FixedFoldOption): string | return match } -function omitHeadKey(key: string, opt: FixedFoldOption): string { +function omitHeadKey(key: string, opt: FixedUnfoldOption): string { const headKey = (() => { const k = extractHeadKey(key, opt) if (typeof k === 'number') { @@ -95,7 +95,7 @@ function omitHeadKey(key: string, opt: FixedFoldOption): string { return key.replace(headKey === undefined ? '' : new RegExp(`^${headKey}\\.?`), '') } -function unfoldInternal(entries: Array<[string, unknown]>, opt: FixedFoldOption): unknown { +function unfoldInternal(entries: Array<[string, unknown]>, opt: FixedUnfoldOption): unknown { if (entries.length <= 0) { return {} } diff --git a/test/fold.spec.ts b/test/fold.spec.ts index 43923dc..6b65d0b 100644 --- a/test/fold.spec.ts +++ b/test/fold.spec.ts @@ -94,4 +94,51 @@ describe('fold', () => { '1.f.h.1': 8 }) }) + + it('nested object with key prefix', () => { + const target = { + a: 1, + b: { + c: 2, + d: [3, 4] + } + } + + expect(fold(target, { keyPrefix: 'root' })).toEqual({ + 'root.a': 1, + 'root.b.c': 2, + 'root.b.d[0]': 3, + 'root.b.d[1]': 4 + }) + }) + + it('nested object with root array with key prefix', () => { + const target = [ + { + a: 1, + b: { + c: 2, + d: [3, 4] + } + }, + { + e: 5, + f: { + g: 6, + h: [7, 8] + } + } + ] as const + + expect(fold(target, { keyPrefix: 'root' })).toEqual({ + 'root[0].a': 1, + 'root[0].b.c': 2, + 'root[0].b.d[0]': 3, + 'root[0].b.d[1]': 4, + 'root[1].e': 5, + 'root[1].f.g': 6, + 'root[1].f.h[0]': 7, + 'root[1].f.h[1]': 8 + }) + }) })