diff --git a/benchmarks/performance/invertBy.bench.ts b/benchmarks/performance/invertBy.bench.ts new file mode 100644 index 000000000..9f0220488 --- /dev/null +++ b/benchmarks/performance/invertBy.bench.ts @@ -0,0 +1,15 @@ +import { bench, describe } from 'vitest'; +import { invertBy as invertByWithCompatToolkit } from 'es-toolkit/compat'; +import { invertBy as invertByWithLodash } from 'lodash'; + +const object = { a: 1, b: 2, c: 1, d: 4 }; + +describe('invertBy', () => { + bench('lodash/invertBy', () => { + invertByWithLodash(object); + }); + + bench('es-toolkit/compat/invertBy', () => { + invertByWithCompatToolkit(object); + }); +}); diff --git a/docs/ja/reference/compat/object/invertBy.md b/docs/ja/reference/compat/object/invertBy.md new file mode 100644 index 000000000..f4720f1e4 --- /dev/null +++ b/docs/ja/reference/compat/object/invertBy.md @@ -0,0 +1,40 @@ +# invertBy + +::: info +この関数は互換性のために `es-toolkit/compat` からのみインポートできます。代替可能なネイティブ JavaScript API があるか、まだ十分に最適化されていないためです。 + +`es-toolkit/compat` からこの関数をインポートすると、[lodash と完全に同じように動作](../../../compatibility.md)します。 +::: + +与えられたオブジェクトのキーと値を [invert](../../object/invert.md) 関数のように反転させた新しいオブジェクトを生成します。 + +値がどのようにキーに反転されるかを `iteratee` 関数で指定します。`iteratee` 関数が与えられない場合、値はそのままキーとして使用されます。 + +新しいオブジェクトの値は、`iteratee` 関数が返した値が同じキーの配列になります。 + +## インターフェース + +```typescript +function invertBy(object: Record, iteratee?: (value: V) => string): Record; +``` + +### パラメータ + +- `object` (`Record`): 反転するオブジェクト。 +- `iteratee` (`(value: V) => string`): オブジェクトのキーとして反転される値を別の文字列に変換する方法を指定する関数。提供されない場合、値はそのままキーとして反転されます。 + +### 戻り値 + +(`Record`): キーと値が反転したオブジェクト。キーは `iteratee` 関数で変換された値になり、値は `iteratee` 関数が返した値が同じキーの配列です。 + +## 例 + +```typescript +const obj = { a: 1, b: 2, c: 1 }; +const result = invertBy(obj); +// result => { '1': ['a', 'c'], '2': ['b'] } + +const obj = { a: 1, b: 2, c: 1 }; +const result = invertBy(obj, value => `group${value}`); +// result => { 'group1': ['a', 'c'], 'group2': ['b'] } +``` diff --git a/docs/ko/reference/compat/object/invertBy.md b/docs/ko/reference/compat/object/invertBy.md new file mode 100644 index 000000000..efa97d0c3 --- /dev/null +++ b/docs/ko/reference/compat/object/invertBy.md @@ -0,0 +1,40 @@ +# invertBy + +::: info +이 함수는 호환성을 위한 `es-toolkit/compat` 에서만 가져올 수 있어요. 대체할 수 있는 네이티브 JavaScript API가 있거나, 아직 충분히 최적화되지 않았기 때문이에요. + +`es-toolkit/compat`에서 이 함수를 가져오면, [lodash와 완전히 똑같이 동작](../../../compatibility.md)해요. +::: + +주어진 객체의 키와 값을 [invert](../../object/invert.md) 함수처럼 뒤집은 새로운 객체를 생성해요. + +값이 어떻게 키로 뒤집힐지를 `iteratee` 함수로 지정해요. `iteratee` 함수가 주어지지 않으면, 값이 그대로 키로 사용돼요. + +새로운 객체의 값은, `iteratee` 함수가 반환한 값이 같은 키들의 배열이 돼요. + +## 인터페이스 + +```typescript +function invertBy(object: Record, iteratee?: (value: V) => string): Record; +``` + +### 파라미터 + +- `object` (`Record`): 값을 뒤집을 객체. +- `iteratee` (`(value: V) => string`): 객체의 키로 뒤집힐 값을 다른 문자열로 바꾸는 방법을 지정하는 함수. 제공되지 않으면, 값이 그대로 키로 뒤집혀요. + +### 반환 값 + +(`Record`): 키와 값이 뒤집힌 객체. 키는 `iteratee` 함수로 변환된 값이 되고, 값은 `iteratee` 함수가 반환한 값이 같은 키들의 배열이에요. + +## 예시 + +```typescript +const obj = { a: 1, b: 2, c: 1 }; +const result = invertBy(obj); +// result => { '1': ['a', 'c'], '2': ['b'] } + +const obj = { a: 1, b: 2, c: 1 }; +const result = invertBy(obj, value => `group${value}`); +// result => { 'group1': ['a', 'c'], 'group2': ['b'] } +``` diff --git a/docs/reference/compat/object/invertBy.md b/docs/reference/compat/object/invertBy.md new file mode 100644 index 000000000..837f8f272 --- /dev/null +++ b/docs/reference/compat/object/invertBy.md @@ -0,0 +1,41 @@ +# invertBy + +::: info +This function is only available in `es-toolkit/compat` for compatibility reasons. It either has alternative native JavaScript APIs or isn’t fully optimized yet. + +When imported from `es-toolkit/compat`, it behaves exactly like lodash and provides the same functionalities, as detailed [here](../../../compatibility.md). +::: + +Creates a new object that reverses the keys and values of the given object, similar to the [invert](../../object/invert.md) function. + +The `iteratee` function specifies how the values are reversed into keys. If no `iteratee` function is provided, the values are used as keys as-is. + +The values of the new object are arrays of keys that correspond to the value returned by the `iteratee` function. + +## Signature + +```typescript +function invertBy(object: Record, iteratee?: (value: V) => string): Record; +``` + +### Parameters + +- `object` (`Record`): The object to iterate over. +- `iteratee` (`(value: V) => string`): A function applied to each value to generate a key. If not provided, defaults to a function that converts the value to a string. + +### Returns + +- `(Record)`: An object where each key is the result of the iteratee, and the corresponding value is an array of property names from the input object that produced that value. + +### Examples + +```typescript +import { invertBy } from 'es-toolkit/compat'; + +const obj = { a: 1, b: 2, c: 1 }; +const result = invertBy(obj); +console.log(result); // => { '1': ['a', 'c'], '2': ['b'] } + +const customResult = invertBy(obj, value => `group${value}`); +console.log(customResult); // => { 'group1': ['a', 'c'], 'group2': ['b'] } +``` diff --git a/docs/zh_hans/reference/compat/object/invertBy.md b/docs/zh_hans/reference/compat/object/invertBy.md new file mode 100644 index 000000000..9e688e44a --- /dev/null +++ b/docs/zh_hans/reference/compat/object/invertBy.md @@ -0,0 +1,40 @@ +# invertBy + +::: info +出于兼容性原因,此函数仅在 `es-toolkit/compat` 中提供。它可能具有替代的原生 JavaScript API,或者尚未完全优化。 + +从 `es-toolkit/compat` 导入时,它的行为与 lodash 完全一致,并提供相同的功能,详情请见 [这里](../../../compatibility.md)。 +::: + +给定对象的键和值会像 [invert](../../object/invert.md) 函数一样被反转,生成一个新的对象。 + +如何将值反转为键由 `iteratee` 函数指定。如果未提供 `iteratee` 函数,则值将直接用作键。 + +新对象的值是 `iteratee` 函数返回的值对应的键的数组。 + +## 签名 + +```typescript +function invertBy(object: Record, iteratee?: (value: V) => string): Record; +``` + +### 参数 + +- `object` (`Record`): 需要反转的对象。 +- `iteratee` (`(value: V) => string`): 指定如何将对象的值转换为其他字符串的函数。如果未提供,则值将直接用作键。 + +### 返回值 + +(`Record`): 反转后的对象。键是通过 `iteratee` 函数转换的值,值是与这些值相同的键的数组。 + +## 示例 + +```typescript +const obj = { a: 1, b: 2, c: 1 }; +const result = invertBy(obj); +// result => { '1': ['a', 'c'], '2': ['b'] } + +const obj = { a: 1, b: 2, c: 1 }; +const result = invertBy(obj, value => `group${value}`); +// result => { 'group1': ['a', 'c'], 'group2': ['b'] } +``` diff --git a/src/compat/index.ts b/src/compat/index.ts index 8d13237af..8dad85957 100644 --- a/src/compat/index.ts +++ b/src/compat/index.ts @@ -77,6 +77,7 @@ export { mergeWith } from './object/mergeWith.ts'; export { fromPairs } from './object/fromPairs.ts'; export { unset } from './object/unset.ts'; export { cloneDeep } from './object/cloneDeep.ts'; +export { invertBy } from './object/invertBy.ts'; export { isPlainObject } from './predicate/isPlainObject.ts'; export { isArray } from './predicate/isArray.ts'; diff --git a/src/compat/object/invertBy.spec.ts b/src/compat/object/invertBy.spec.ts new file mode 100644 index 000000000..d4b233eda --- /dev/null +++ b/src/compat/object/invertBy.spec.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from 'vitest'; +import { invertBy } from './invertBy.ts'; + +/** + * @see https://github.com/lodash/lodash/blob/6a2cc1dfcf7634fea70d1bc5bd22db453df67b42/test/invertBy.spec.js + */ + +describe('invertBy', () => { + const object = { a: 1, b: 2, c: 1 }; + + it('should transform keys by `iteratee`', () => { + const expected = { group1: ['a', 'c'], group2: ['b'] }; + + const actual = invertBy(object, value => `group${value}`); + + expect(actual).toEqual(expected); + }); + + it('should use `identity` when `iteratee` is nullish', () => { + const values = [undefined, null]; + const expected = values.map(() => ({ '1': ['a', 'c'], '2': ['b'] })); + + const actual = values.map((value, index) => (index ? invertBy(object, value as any) : invertBy(object))); + + expect(actual).toEqual(expected); + }); + + it('should only add multiple values to own, not inherited, properties', () => { + const objectWithInheritedProps = { a: 'hasOwnProperty', b: 'constructor' }; + const expected = { hasOwnProperty: ['a'], constructor: ['b'] }; + + const actual = invertBy(objectWithInheritedProps); + + expect(actual).toEqual(expected); + }); +}); diff --git a/src/compat/object/invertBy.ts b/src/compat/object/invertBy.ts new file mode 100644 index 000000000..eabfaef5b --- /dev/null +++ b/src/compat/object/invertBy.ts @@ -0,0 +1,58 @@ +import { isNil } from '../../predicate/isNil.ts'; +import { identity } from '../_internal/identity.ts'; + +/** + * Creates a new object that reverses the keys and values of the given object, similar to the invert. + * + * The `iteratee` function specifies how the values are reversed into keys. If no `iteratee` function is provided, the values are used as keys as-is. + * + * The values of the new object are arrays of keys that correspond to the value returned by the `iteratee` function. + * + * @param {Record} object - The object to iterate over. + * @param {(value: V) => string} [iteratee] - Optional. A function that generates a key based on each value in the object. + * If not provided, the function defaults to using the value as a string. + * + * @returns {Record} An object where the keys are generated by the iteratee, and the values + * are arrays of property names (keys) from the input object that correspond to those keys. + * + * @example + * const obj = { a: 1, b: 2, c: 1 }; + * const result = invertBy(obj); + * // result => { '1': ['a', 'c'], '2': ['b'] } + * + * @example + * const obj = { a: 1, b: 2, c: 1 }; + * const result = invertBy(obj, value => `group${value}`); + * // result => { 'group1': ['a', 'c'], 'group2': ['b'] } + */ +export function invertBy( + object: Record, + iteratee?: (value: V) => string +): Record { + const result = {} as Record; + + if (isNil(object)) { + return result; + } + + if (iteratee == null) { + iteratee = identity as (value: V) => string; + } + + const keys = Object.keys(object); + + for (let i = 0; i < keys.length; i++) { + const key = keys[i] as K; + + const value = object[key]; + const valueStr = iteratee(value); + + if (Array.isArray(result[valueStr])) { + result[valueStr].push(key); + } else { + result[valueStr] = [key]; + } + } + + return result; +}