Skip to content

Commit

Permalink
feat(map): implement map (#860)
Browse files Browse the repository at this point in the history
* feat(map): implement map

* merge

* make lint happy

* coverage

* update map

---------

Co-authored-by: Sojin Park <raon0211@gmail.com>
Co-authored-by: Sojin Park <raon0211@toss.im>
  • Loading branch information
3 people authored Dec 7, 2024
1 parent f7abb57 commit e387b4f
Show file tree
Hide file tree
Showing 9 changed files with 931 additions and 2 deletions.
63 changes: 63 additions & 0 deletions benchmarks/performance/map.bench.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { bench, describe } from 'vitest';
import { map as mapToolkit_ } from 'es-toolkit/compat';
import { map as mapLodash_ } from 'lodash';

const mapToolkit = mapToolkit_;
const mapLodash = mapLodash_;

const generateArray = (length: number) => Array.from({ length }, (_, i) => i);
const generateObject = (length: number) => Object.fromEntries(generateArray(length).map(i => [`key${i}`, i]));

describe('map/array', () => {
const array = [1, 2, 3, 4, 5];

bench('es-toolkit/map', () => {
mapToolkit(array, value => value * 2);
});

bench('lodash/map', () => {
mapLodash(array, value => value * 2);
});
});

describe('map/object', () => {
const obj = {
key1: 1,
key2: 2,
key3: 3,
key4: 4,
key5: 5,
};

bench('es-toolkit/map', () => {
mapToolkit(obj, value => value * 2);
});

bench('lodash/map', () => {
mapLodash(obj, value => value * 2);
});
});

describe('map/largeArray', () => {
const largeArray = generateArray(10000);

bench('es-toolkit/map', () => {
mapToolkit(largeArray, value => value * 2);
});

bench('lodash/map', () => {
mapLodash(largeArray, value => value * 2);
});
});

describe('map/largeObject', () => {
const largeObject = generateObject(10000);

bench('es-toolkit/map', () => {
mapToolkit(largeObject, value => value * 2);
});

bench('lodash/map', () => {
mapLodash(largeObject, value => value * 2);
});
});
96 changes: 96 additions & 0 deletions docs/ja/reference/compat/array/map.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# map

::: info
この関数は互換性のために `es-toolkit/compat` からのみインポートできます。代替可能なネイティブ JavaScript API があるか、まだ十分に最適化されていないためです。

`es-toolkit/compat` からこの関数をインポートすると、[lodash と完全に同じように動作](../../../compatibility.md)します。
:::
配列の各要素を変換して新しい配列を返します。

各要素を変換する方法は [iteratee](../util/iteratee.md) 関数の動作に従って指定できます。

- **変換関数**: 各要素に対して指定された関数を実行し、その結果に変換します。
- **プロパティ名**: 各要素から指定されたプロパティ名を選択します。
- **プロパティ-値ペア**: 各要素のプロパティが指定された値と一致するかどうかを真偽値で返します。
- **部分オブジェクト**: 各要素が部分オブジェクトのプロパティと値に一致するかどうかを真偽値で返します。

## インターフェース

```typescript
function map<T, U>(arr: T[], iteratee: (value: T, index: number, arr: T[]) => U): U[];
function map<T>(arr: T[], iteratee: Partial<T>): boolean[];
function map<T>(arr: T[], iteratee: [keyof T, unknown]): boolean[];
function map<T, K extends keyof T>(arr: T[], iteratee: K): Array<T[K]>;
function map<T>(arr: T[], iteratee?: null | undefined): T[];

function map<T extends object, U>(object: T, iteratee: (value: T[keyof T], key: string, object: T) => U): U[];
function map<T>(object: T, iteratee: Partial<T[keyof T]>): boolean[];
function map<T>(object: T, iteratee: [keyof T[keyof T], unknown]): boolean[];
function map<T, K extends keyof T[keyof T]>(object: T, iteratee: K): Array<T[keyof T][K]>;
function map<T extends object, U>(object: T, iteratee?: null | undefined): U[];
```

### パラメータ

- `arr` (`T[]`) または `object` (`T`): 変換する配列またはオブジェクト。

::: info `arr``ArrayLike<T>` であるか、`null` または `undefined` である可能性があります

lodash と完全に互換性があるように、`map` 関数は `arr` を次のように処理します:

- `arr``ArrayLike<T>` の場合、`Array.from(...)` を使用して配列に変換します。
- `arr``null` または `undefined` の場合、空の配列と見なされます。

:::

::: info `object``null` または `undefined` である可能性があります

lodash と完全に互換性があるように、`map` 関数は `object` を次のように処理します:

- `object``null` または `undefined` の場合、空のオブジェクトに変換されます。

:::

- `iteratee`:

- 配列の場合:

- **変換関数** (`(value: T, index: number, arr: T[]) => U`): 配列の各要素を変換する関数。
- **プロパティ名** (`keyof T`): 各要素から選択するプロパティ名。
- **プロパティ-値ペア** (`[keyof T, unknown]`): 最初が一致させるプロパティ、2番目が一致させる値を表すタプル。
- **部分オブジェクト** (`Partial<T>`): 一致させるプロパティと値を指定した部分オブジェクト。

- オブジェクトの場合:

- **変換関数** (`(item: T[keyof T], index: number, object: T) => unknown`): オブジェクトの各値を変換する関数。
- **プロパティ名** (`keyof T[keyof T]`): オブジェクトの各値から選択するプロパティ名。
- **プロパティ-値ペア** (`[keyof T[keyof T], unknown]`): 最初が一致させるプロパティ、2番目が一致させる値を表すタプル。
- **部分オブジェクト** (`Partial<T[keyof T]>`): 一致させるプロパティと値を指定した部分オブジェクト。

### 戻り値

(`any[]`): 変換された値の新しい配列。

## 使用例

```typescript
// 変換関数使用
const array = [1, 2, 3];
map(array, value => value * 2); // => [2, 4, 6]

// イテレータでプロパティキーを使用
const objects = [{ a: 1 }, { a: 2 }, { a: 3 }];
map(objects, 'a'); // => [1, 2, 3]

// イテレータでオブジェクトを使用
const objects = [{ a: 1 }, { a: 2 }, { a: 3 }];
map(objects, { a: 1 }); // => [true, false, false]

// イテレータなし
const numbers = [1, 2, 3];
map(numbers); // => [1, 2, 3]

// オブジェクトでコレクションを使用
const obj = { a: 1, b: 2, c: 3 };
map(obj, (value, key) => `${key}: ${value}`); // => ['a: 1', 'b: 2', 'c: 3']
```
97 changes: 97 additions & 0 deletions docs/ko/reference/compat/array/map.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# map

::: info
이 함수는 호환성을 위한 `es-toolkit/compat` 에서만 가져올 수 있어요. 대체할 수 있는 네이티브 JavaScript API가 있거나, 아직 충분히 최적화되지 않았기 때문이에요.

`es-toolkit/compat`에서 이 함수를 가져오면, [lodash와 완전히 똑같이 동작](../../../compatibility.md)해요.
:::

배열의 각 요소를 변환해서 새로운 배열을 반환해요.

각 요소를 변환하는 방법은 [iteratee](../util/iteratee.md) 함수의 동작에 따라 명시할 수 있어요.

- **변환 함수**: 각 요소에 대해 주어진 함수를 실행해서, 그 결괏값으로 변환해요.
- **프로퍼티 이름**: 각 요소에서 주어진 프로퍼티 이름을 선택해요.
- **프로퍼티-값 쌍**: 각 요소의 프로퍼티가 주어진 값과 일치하는지에 대한 참/거짓 값으로 변환해요.
- **부분 객체**: 각 요소가 부분 객체의 프로퍼티와 값에 일치하는지에 대한 참/거짓 값으로 변환해요.

## 인터페이스

```typescript
function map<T, U>(arr: T[], iteratee: (value: T, index: number, arr: T[]) => U): U[];
function map<T>(arr: T[], iteratee: Partial<T>): boolean[];
function map<T>(arr: T[], iteratee: [keyof T, unknown]): boolean[];
function map<T, K extends keyof T>(arr: T[], iteratee: K): Array<T[K]>;
function map<T>(arr: T[], iteratee?: null | undefined): T[];

function map<T extends object, U>(object: T, iteratee: (value: T[keyof T], key: string, object: T) => U): U[];
function map<T>(object: T, iteratee: Partial<T[keyof T]>): boolean[];
function map<T>(object: T, iteratee: [keyof T[keyof T], unknown]): boolean[];
function map<T, K extends keyof T[keyof T]>(object: T, iteratee: K): Array<T[keyof T][K]>;
function map<T extends object, U>(object: T, iteratee?: null | undefined): U[];
```

### 파라미터

- `arr` (`T[]`) 또는 `object` (`T`): 변환할 배열이나 객체.

::: info `arr``ArrayLike<T>`일 수도 있고, `null` 또는 `undefined`일 수도 있어요

lodash와 완벽하게 호환되도록 `map` 함수는 `arr`을 다음과 같이 처리해요:

- `arr``ArrayLike<T>`인 경우 `Array.from(...)`을 사용하여 배열로 변환해요.
- `arr``null` 또는 `undefined`인 경우 빈 배열로 간주돼요.

:::

::: info `object``null` 또는 `undefined`일 수도 있어요

lodash와 완벽하게 호환되도록 `map` 함수는 `object`를 다음과 같이 처리해요:

- `object``null` 또는 `undefined`인 경우 빈 객체로 변환돼요.

:::

- `iteratee`:

- 배열의 경우:

- **변환 함수** (`(value: T, index: number, arr: T[]) => U`): 배열의 각 요소를 변환할 함수.
- **프로퍼티 이름** (`keyof T`): 각 요소에서 선택할 프로퍼티 이름.
- **프로퍼티-값 쌍** (`[keyof T, unknown]`): 첫 번째가 일치시킬 프로퍼티, 두 번째가 일치시킬 값을 나타내는 튜플.
- **부분 객체** (`Partial<T>`): 일치시킬 프로퍼티와 값들을 명시한 부분 객체.

- 객체의 경우:

- **변환 함수** (`(item: T[keyof T], index: number, object: T) => unknown`): 객체의 각 값을 변환할 함수.
- **프로퍼티 이름** (`keyof T[keyof T]`): 객체의 각 값에서 선택할 프로퍼티 이름.
- **프로퍼티-값 쌍** (`[keyof T[keyof T], unknown]`): 첫 번째가 일치시킬 프로퍼티, 두 번째가 일치시킬 값을 나타내는 튜플.
- **부분 객체** (`Partial<T[keyof T]>`): 일치시킬 프로퍼티와 값들을 명시한 부분 객체.

### 반환 값

(`any[]`): 변환된 값의 새 배열.

### 예시

```typescript
// 변환 함수 사용
const array = [1, 2, 3];
map(array, value => value * 2); // => [2, 4, 6]

// 이터레이터로 속성 키 사용
const objects = [{ a: 1 }, { a: 2 }, { a: 3 }];
map(objects, 'a'); // => [1, 2, 3]

// 이터레이터로 객체 사용
const objects = [{ a: 1 }, { a: 2 }, { a: 3 }];
map(objects, { a: 1 }); // => [true, false, false]

// 이터레이터 없음
const numbers = [1, 2, 3];
map(numbers); // => [1, 2, 3]

// 객체로 컬렉션 사용
const obj = { a: 1, b: 2, c: 3 };
map(obj, (value, key) => `${key}: ${value}`); // => ['a: 1', 'b: 2', 'c: 3']
```
4 changes: 2 additions & 2 deletions docs/ko/reference/compat/util/iteratee.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
`iteratee` 함수에 주어지는 파라미터의 종류에 따라서 반환하는 함수의 동작이 달라져요.

- **함수**: 주어진 함수를 있는 그대로 반환해요.
- **속성 이름**: 요소에서 주어진 프로퍼티의 값을 반환해요.
- **속성-값 쌍**: 요소의 속성이 주어진 값과 일치하는지 여부를 나타내는 참/거짓 값을 반환해요.
- **프로퍼티 이름**: 요소에서 주어진 프로퍼티의 값을 반환해요.
- **프로퍼티-값 쌍**: 요소의 프로퍼티가 주어진 값과 일치하는지 여부를 나타내는 참/거짓 값을 반환해요.
- **부분 객체**: 요소가 부분 객체의 프로퍼티와 값에 일치하는지 여부를 나타내는 참/거짓 값을 반환해요.

아무 인수도 제공하지 않거나 `null`을 전달하면, 이 함수는 [요소를 있는 그대로 반환하는 함수](../../function/identity.md)를 반환해요.
Expand Down
97 changes: 97 additions & 0 deletions docs/reference/compat/array/map.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# map

::: 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).
:::

Transforms each element of an array and returns a new array.

The way each element is transformed can be specified according to the behavior of the [iteratee](../util/iteratee.md) function.

- **Transformation function**: Executes a given function on each element and transforms it to the result.
- **Property name**: Selects the specified property name from each element.
- **Property-value pair**: Transforms each element to a boolean indicating whether the element's property matches the given value.
- **Partial object**: Transforms each element to a boolean indicating whether the element matches the properties and values of the partial object.

## Signature

```typescript
function map<T, U>(arr: T[], iteratee: (value: T, index: number, arr: T[]) => U): U[];
function map<T>(arr: T[], iteratee: Partial<T>): boolean[];
function map<T>(arr: T[], iteratee: [keyof T, unknown]): boolean[];
function map<T, K extends keyof T>(arr: T[], iteratee: K): Array<T[K]>;
function map<T>(arr: T[], iteratee?: null | undefined): T[];

function map<T extends object, U>(object: T, iteratee: (value: T[keyof T], key: string, object: T) => U): U[];
function map<T>(object: T, iteratee: Partial<T[keyof T]>): boolean[];
function map<T>(object: T, iteratee: [keyof T[keyof T], unknown]): boolean[];
function map<T, K extends keyof T[keyof T]>(object: T, iteratee: K): Array<T[keyof T][K]>;
function map<T extends object, U>(object: T, iteratee?: null | undefined): U[];
```

### Parameters

- `arr` (`T[]`) or `object` (`Record<PropertyKey, T>`): The array or object to transform.

::: info `arr` can be `ArrayLike<T>`, `null`, or `undefined`

To ensure full compatibility with lodash, the `map` function handles `arr` as follows:

- If `arr` is `ArrayLike<T>`, it is converted to an array using `Array.from(...)`.
- If `arr` is `null` or `undefined`, it is treated as an empty array.

:::

::: info `object` can be `null` or `undefined`

To ensure full compatibility with lodash, the `map` function handles `object` as follows:

- If `object` is `null` or `undefined`, it is converted to an empty object.

:::

- `iteratee`:

- For arrays:

- **Transformation function** (`(value: T, index: number, arr: T[]) => U`): A function to transform each element of the array.
- **Property name** (`keyof T`): The property name to select from each element.
- **Property-value pair** (`[keyof T, unknown]`): A tuple where the first element is the property to match, and the second is the value to match.
- **Partial object** (`Partial<T>`): A partial object specifying properties and values to match.

- For objects:

- **Transformation function** (`(item: T[keyof T], index: number, object: T) => unknown`): A function to transform each value of the object.
- **Property name** (`keyof T[keyof T]`): The property name to select from each value of the object.
- **Property-value pair** (`[keyof T[keyof T], unknown]`): A tuple where the first element is the property to match, and the second is the value to match.
- **Partial object** (`Partial<T[keyof T]>`): A partial object specifying properties and values to match.

### Returns

(`any[]`): A new array of transformed values.

### Example

```typescript
// Using a transformation function
const array = [1, 2, 3];
map(array, value => value * 2); // => [2, 4, 6]

// Using a property key as the iteratee
const objects = [{ a: 1 }, { a: 2 }, { a: 3 }];
map(objects, 'a'); // => [1, 2, 3]

// Using an object as the iteratee
const objects = [{ a: 1 }, { a: 2 }, { a: 3 }];
map(objects, { a: 1 }); // => [true, false, false]

// No iteratee
const numbers = [1, 2, 3];
map(numbers); // => [1, 2, 3]

// Using an object as the collection
const obj = { a: 1, b: 2, c: 3 };
map(obj, (value, key) => `${key}: ${value}`); // => ['a: 1', 'b: 2', 'c: 3']
```
Loading

0 comments on commit e387b4f

Please sign in to comment.