Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: #30 add function "pick", "omit" #31

Merged
merged 4 commits into from
Oct 5, 2023
Merged

feat: #30 add function "pick", "omit" #31

merged 4 commits into from
Oct 5, 2023

Conversation

mew-ton
Copy link
Contributor

@mew-ton mew-ton commented Oct 5, 2023

Summary by CodeRabbit

リリースノート:

  • 新機能: pick関数を追加しました。指定したキーを持つ新しいオブジェクトを生成します。
  • 新機能: omit関数を追加しました。指定したキーを除去した新しいオブジェクトを生成します。
  • 新機能: unfold, twist, pick, omit関数にpruneArrayオプションを追加しました。配列要素の削除を制御できます。
  • テスト: omitpick関数のテストスイートを追加しました。
  • リファクタリング: タイプインポートの再構成と、unfold関数のdefaultUnfoldOptionの使用を更新しました。
  • リファクタリング: twist関数のシグネチャを更新し、ジェネリック型パラメータMを追加しました。

@changeset-bot
Copy link

changeset-bot bot commented Oct 5, 2023

🦋 Changeset detected

Latest commit: a948760

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
json-origami Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link

coderabbitai bot commented Oct 5, 2023

Walkthrough

この変更では、json-origamiライブラリに新たな関数pickomitが追加されました。これらの関数は、JSONオブジェクトから特定のキーを選択または削除する機能を提供します。また、unfoldtwistpickomit関数で配列要素を削除するかどうかを制御するpruneArrayオプションも追加されました。

Changes

ファイル 概要
README.md 新しい関数pickomitの紹介と説明
src/index.ts omitpickのエクスポートの追加
src/omit.ts, src/pick.ts omitpick関数の実装
src/twist.ts, src/unfold.ts 関数のインポートとシグネチャの更新
src/type.ts 新しい型の追加と既存の型の更新
src/utils.ts キーの包含関係を確認する新しい関数includesKeyの追加
test/*.spec.ts 新しい関数のテストケースの追加と既存のテストケースの更新
.changeset/chatty-dryers-tickle.md 新機能の追加に関する情報

🐇💻

新たな道を切り開く、コードのウサギ🐇

キーを選び、キーを省き、

データの海を巧みに航海⛵️

さあ、新しい冒険が始まる!🎉


Tips

Chat with CodeRabbit Bot (@coderabbitai)

  • Mention @coderabbitai in any review comment for bot assistance.
  • Note: Review comments are made on code diffs or files, not on the PR overview.

Pause Incremental Reviews

  • Insert @coderabbitai: ignore in the PR description to halt the bot's ongoing reviews. Remove the line to resume.
  • Additionally, enter @coderabbitai pause to halt the bot's ongoing reviews as any issue or review comment. To
    resume reviews, enter @coderabbitai resume as any issue or review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 3

Commits Files that changed from the base of the PR and between a7bd2c0 and a4fe7f5.
Files ignored due to filter (2)
  • package.json
  • yarn.lock
Files selected for processing (13)
  • README.md (6 hunks)
  • src/index.ts (1 hunks)
  • src/omit.ts (1 hunks)
  • src/pick.ts (1 hunks)
  • src/twist.ts (1 hunks)
  • src/type.ts (5 hunks)
  • src/unfold.ts (4 hunks)
  • src/utils.ts (1 hunks)
  • test/fold.spec.ts (6 hunks)
  • test/omit.spec.ts (1 hunks)
  • test/pick.spec.ts (1 hunks)
  • test/twist.spec.ts (3 hunks)
  • test/unfold.spec.ts (5 hunks)
Files skipped from review due to trivial changes (6)
  • src/index.ts
  • src/unfold.ts
  • test/fold.spec.ts
  • test/omit.spec.ts
  • test/pick.spec.ts
  • test/unfold.spec.ts
Additional comments (Suppressed): 15
src/twist.ts (2)
  • 10-13: MoveMapの型がM extends MoveMap<D>に変更されています。この変更が意図したものであること、そしてこれがコードベース全体で適切に反映されていることを確認してください。

  • 1-2: foldunfoldのインポートパスが変更されました。新しいパスが正しいことを確認してください。

README.md (3)
  • 11-15: 新しい機能の説明が削除されています。pickomit関数についての説明を追加することを検討してください。

  • 88-126: 新たな関数pickomitの使用例が追加されました。これらは、指定したキーをJSONオブジェクトから選択または削除する機能を提供します。

  • 153-180: 新たなオプションpruneArrayの使用例が追加されました。このオプションは、配列要素を削除するかどうかを制御します。

src/utils.ts (1)
  • 1-30: includesKey関数は、指定したキーが元のキーに含まれているかどうかをチェックします。この関数は、配列インデックスを扱うためのオプションを受け取り、キーがブラケット形式で表現されている場合にそれをドット形式に変換します。その後、キーを分割し、元のキーと比較します。これは、キーのネストをフラットにするための効率的な方法です。

ただし、この関数は、キーが部分文字列として元のキーに存在する場合でもfalseを返す可能性があります。例えば、includesKey("foo.bar.baz", "ba", { arrayIndex: "bracket" })falseを返します。これは意図した動作であるか確認してください。

また、この関数は大量のデータに対して実行される場合、パフォーマンスに影響を及ぼす可能性があります。特に、split関数が2回呼び出され、それぞれの呼び出しで新しい配列が生成されます。これらの配列は、最終的にはGCによってクリーンアップされますが、大量のデータに対してこの関数が頻繁に呼び出される場合、メモリ使用量とGCの負荷が増加する可能性があります。この問題を改善するためには、split関数の呼び出しを1回に減らすか、または配列の代わりにイテレータを使用することを検討してみてください。

test/twist.spec.ts (5)
  • 113-150: 新しいテストケースが追加されています。このテストは、ルートレベルで数値と文字列のキーを持つオブジェクトを処理する能力を検証します。特定のキーを変換しながら、元のオブジェクト構造の残りの部分を保持することを確認します。

  • 152-193: 新しいテストケースが追加されています。このテストは、ルートレベルで数値と文字列のキーを持つオブジェクトを処理する能力を検証しますが、配列インデックスにドット記法を使用します。特定のキーを変換しながら、元のオブジェクト構造の残りの部分を保持することを確認します。

  • 195-212: 新しいテストケースが追加されています。このテストは、pruneArrayオプションがtrueに設定されている場合に、配列要素が削除されることを検証します。特定のキーを変換しながら、元のオブジェクト構造の残りの部分を保持することを確認します。

  • 214-230: 新しいテストケースが追加されています。このテストは、pruneArrayオプションがfalseに設定されている場合に、配列要素が削除されないことを検証します。特定のキーを変換しながら、元のオブジェクト構造の残りの部分を保持することを確認します。

  • 239-250: > Note: This review was outside of the patch, so it was mapped to the patch with the greatest overlap. Original lines [233-248]

新しいテストケースが追加されています。このテストは、第二引数が空のオブジェクトである場合、twist関数が元のオブジェクトをそのまま返すことを検証します。

src/type.ts (4)
  • 12-23: 新たにDeepKeyOf型が追加されましたが、現在は単純な文字列として定義されています。深くネストしたキーをサポートするためのロジックがまだ実装されていないようです。このTODOコメントを確認し、必要なロジックを追加してください。

  • 30-33: MoveMap型が更新され、DeepKeyOf<D>を使用するようになりました。これにより、深くネストしたキーをサポートできるようになります。ただし、DeepKeyOfの実装が完了していないため、この変更が期待通りに機能するか確認してください。

  • 92-113: UnfoldOptionインターフェースに新たにpruneArrayオプションが追加されました。これにより、配列が空の場合に削除するかどうかを制御できます。この新しいオプションが正しく動作するかテストしてください。

  • 125-141: TwistOption, OmitOption, PickOptionインターフェースが追加され、それぞれがUnfoldOptionを拡張しています。これにより、これらのオプションは新たに追加されたpruneArrayオプションも利用できます。これらの新しいインターフェースが期待通りに機能するか確認してください。

src/pick.ts Outdated Show resolved Hide resolved
src/omit.ts Outdated Show resolved Hide resolved
src/type.ts Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Commits Files that changed from the base of the PR and between a4fe7f5 and fcdc88f.
Files selected for processing (1)
  • .changeset/chatty-dryers-tickle.md (1 hunks)
Files skipped from review due to trivial changes (1)
  • .changeset/chatty-dryers-tickle.md

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 2

Commits Files that changed from the base of the PR and between fcdc88f and a948760.
Files selected for processing (3)
  • src/omit.ts (1 hunks)
  • src/pick.ts (1 hunks)
  • src/utils.ts (1 hunks)
Files skipped from review due to trivial changes (1)
  • src/utils.ts

Comment on lines +33 to +49
export function pick<D extends Dictionary, K extends DeepKeyOf<D>>(
obj: D,
keys: K[],
opt?: PickOption
): Omit<D, K> {
const folded = fold(obj)

const targetKeys = new Set(
Object.keys(folded).filter((k) => keys.some((key) => includesKey(key, k, opt)))
)

const fixedKeyMap = Object.fromEntries(
Object.entries(folded).filter(([k]) => targetKeys.has(k))
) as Folded<Dictionary>

return unfold(fixedKeyMap, opt) as Dictionary
}
Copy link

@coderabbitai coderabbitai bot Oct 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pick関数の実装は、指定されたキーを持つオブジェクトを正しく生成するように見えます。しかし、パフォーマンスの観点から、行41でkeys.some()を使用している部分が問題となります。このコードは、各キーに対して全てのターゲットキーを走査し、O(n^2)の時間複雑度を持ちます。これを改善するために、keysをSetに変換し、その存在チェックをO(1)の操作にすることを検討してみてください。

-  const targetKeys = new Set(
-    Object.keys(folded).filter((k) => keys.some((key) => includesKey(key, k, opt)))
-  )
+  const keySet = new Set(keys);
+  const targetKeys = new Set(
+    Object.keys(folded).filter((k) => keySet.has(k))
+  )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

includesKey は文字列の一致比較ではないため、 keySet.has とは判定結果が異なります。

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mew-ton ご指摘いただきありがとうございます。includesKey関数が単純な文字列の一致比較ではないという点は見落としていました。そのため、提案した修正は適切ではありませんでした。

しかし、パフォーマンスの観点からは依然として改善の余地があります。keys.some()の使用は時間複雑度をO(n^2)にするため、この部分を最適化することを検討してみてください。

具体的には、includesKey関数の結果をキャッシュすることで、同じキーに対する重複した計算を避けることができます。以下に示すように、Mapを使用してキーとそれに対応するincludesKeyの結果を保存し、再利用することができます。

+  const includesKeyCache = new Map();
   const targetKeys = new Set(
-    Object.keys(folded).filter((k) => keys.some((key) => includesKey(key, k, opt)))
+    Object.keys(folded).filter((k) => {
+      if (!includesKeyCache.has(k)) {
+        includesKeyCache.set(k, keys.some((key) => includesKey(key, k, opt)));
+      }
+      return includesKeyCache.get(k);
+    })
   )

この変更により、各キーに対するincludesKeyの計算が最大で1回しか行われないため、パフォーマンスが向上する可能性があります。ただし、キャッシュの使用はメモリ使用量を増加させる可能性があるため、そのトレードオフを考慮に入れてください。

Comment on lines +1 to +48
import { fold } from './fold'
import { unfold } from './unfold'
import { includesKey } from './utils'
import type { Dictionary, DeepKeyOf, OmitOption, Omit, Folded } from './type'

/**
* Returns an object with the specified keys removed from the object.
*
* @example
* ```ts
* const obj = {
* a: 1,
* b: {
* c: 2,
* d: [3, 4]
* }
* }
*
* const omitted = omit(obj, ['a', 'b.c'])
* // omitted is
* // {
* // b: {
* // d: [3, 4]
* // }
* // }
* ```
*
* @param obj
* @param keys
* @param opt
*/
export function omit<D extends Dictionary, K extends DeepKeyOf<D>>(
obj: D,
keys: K[],
opt?: OmitOption
): Omit<D, K> {
const folded = fold(obj)

const targetKeys = new Set(
Object.keys(folded).filter((k) => !keys.some((key) => includesKey(key, k, opt)))
)

const fixedKeyMap = Object.fromEntries(
Object.entries(folded).filter(([k]) => targetKeys.has(k))
) as Folded<Dictionary>

return unfold(fixedKeyMap, opt) as Dictionary
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

omit関数の実装は、指定されたキーをオブジェクトから削除するという目的に適しています。この関数は、まずオブジェクトをフラットな表現に変換し(fold関数)、次に指定したキーをフィルタリングし、最後に元の構造に戻します(unfold関数)。これらのステップは効率的であり、コードの可読性も高いです。

ただし、一部のパフォーマンス改善が可能です。特に、keys.some((key) => includesKey(key, k, opt))の部分では、各キーに対してincludesKey関数が呼び出されています。これは、大きなキー配列に対しては非効率的になる可能性があります。代わりに、キーをSetに変換し、その存在をチェックすることで、パフォーマンスを向上させることができます。

以下に修正案を示します:

export function omit<D extends Dictionary, K extends DeepKeyOf<D>>(
  obj: D,
  keys: K[],
  opt?: OmitOption
): Omit<D, K> {
  const folded = fold(obj)

+ const keySet = new Set(keys);
- const targetKeys = new Set(
-   Object.keys(folded).filter((k) => !keys.some((key) => includesKey(key, k, opt)))
- )
+ const targetKeys = new Set(
+   Object.keys(folded).filter((k) => !keySet.has(k))
+ )

  const fixedKeyMap = Object.fromEntries(
    Object.entries(folded).filter(([k]) => targetKeys.has(k))
  ) as Folded<Dictionary>

  return unfold(fixedKeyMap, opt) as Dictionary
}

@mew-ton mew-ton merged commit e5916c1 into main Oct 5, 2023
4 of 5 checks passed
@mew-ton mew-ton deleted the feat/30_pick_omit branch October 5, 2023 08:02
@github-actions github-actions bot mentioned this pull request Oct 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant