-
Notifications
You must be signed in to change notification settings - Fork 316
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(compat): Add map
function
#514
Open
iDevGon
wants to merge
6
commits into
toss:main
Choose a base branch
from
iDevGon:feat/map
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
7a1f570
feat(compat): implement array/map
iDevGon d9b9f24
feat(compat): Add array/map test code
iDevGon d3df5f3
feat(compat): Add array/map docs
iDevGon 6671bd3
feat(compat): Add array/map bench
iDevGon 899587b
Merge branch 'main' into feat/map
iDevGon 7c23430
Merge branch 'main' into feat/map
iDevGon File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { bench, describe } from 'vitest'; | ||
import { map as mapToolkit } from 'es-toolkit/compat'; | ||
import { map as mapLodash } from 'lodash'; | ||
|
||
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 = generateArray(10000); | ||
|
||
bench('es-toolkit/map', () => { | ||
mapToolkit(array, value => value * 2); | ||
}); | ||
|
||
bench('lodash/map', () => { | ||
mapLodash(array, value => value * 2); | ||
}); | ||
}); | ||
|
||
describe('map/object', () => { | ||
const obj = generateObject(10000); | ||
|
||
bench('es-toolkit/map', () => { | ||
mapToolkit(obj, value => value * 2); | ||
}); | ||
|
||
bench('lodash/map', () => { | ||
mapLodash(obj, value => value * 2); | ||
}); | ||
}); |
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,47 @@ | ||
# map | ||
|
||
::: info | ||
이 함수는 호환성을 위한 `es-toolkit/compat` 에서만 가져올 수 있어요. 대체할 수 있는 네이티브 JavaScript API가 있거나, 아직 충분히 최적화되지 않았기 때문이에요. | ||
|
||
`es-toolkit/compat`에서 이 함수를 가져오면, [lodash와 완전히 똑같이 동작](../../../compatibility.md)해요. | ||
::: | ||
|
||
컬렉션의 요소들을 순회하며 변환 함수(이터레이터)를 적용한 결과를 반환하는 배열을 생성해요. | ||
배열, 객체, 기타 여러 타입의 컬렉션과 함께 사용할 수 있어 다양한 데이터 구조에서 호환돼요. | ||
만약 이터레이터가 제공되지 않으면, 컬렉션의 원래 요소들을 반환해요. | ||
|
||
## 인터페이스 | ||
|
||
```typescript | ||
function map<T, U>( | ||
collection: T[] | Record<string, T> | number | string | boolean | null | undefined, | ||
iteratee?: ((value: T, key: string | number) => U) | keyof T | null | ||
): U[]; | ||
``` | ||
|
||
### 파라미터 | ||
- `collection` (`T[] | Record<string, T> | number | string | boolean | null | undefined`): 순회할 컬렉션. 배열이나 객체, 원시 타입이 될 수 있어요. 컬렉션이 `null` 또는 `undefined`인 경우 빈 배열을 반환해요. | ||
- `iteratee` (`(value: T, key: string | number) => U | keyof T | null`, 선택): 각 요소마다 호출되는 변환 함수에요. 함수가 제공되면, 해당 함수가 각 요소에 대해 변환 작업을 수행해요. 만약 이터레이터가 속성 키(예: `keyof T`)라면, 각 요소의 해당 속성 값이 반환돼요. 이터레이터가 제공되지 않으면, 원래 요소들이 그대로 반환돼요. | ||
|
||
### 반환 값 | ||
(`U[]`): 변환된 값들로 이루어진 새로운 배열이에요. | ||
|
||
### 예시 | ||
|
||
```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 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'] | ||
``` |
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,47 @@ | ||
# 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). | ||
::: | ||
|
||
Iterates over elements in a collection and applies a transformation function, returning a new array of results. | ||
This method works with arrays, objects, and other types, ensuring compatibility across different types of collections. | ||
If no iteratee is provided, the function returns the original elements of the collection. | ||
|
||
## Signature | ||
|
||
```typescript | ||
function map<T, U>( | ||
collection: T[] | Record<string, T> | number | string | boolean | null | undefined, | ||
iteratee?: ((value: T, key: string | number) => U) | keyof T | null | ||
): U[]; | ||
``` | ||
|
||
### Parameters | ||
- `collection` (`T[] | Record<string, T> | number | string | boolean | null | undefined`): The collection to iterate over. It can be an array, an object, or a primitive type. If the collection is `null` or `undefined`, an empty array is returned. | ||
- `iteratee` (`(value: T, key: string | number) => U | keyof T | null`, optional): The function invoked per iteration. If it's a function, it will be used to transform each element. If it's a property key (i.e., `keyof T`), the value of that property will be returned for each element. If no iteratee is provided, the function returns the original elements. | ||
|
||
### Returns | ||
(`U[]`): 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] | ||
|
||
// No iteratee | ||
const numbers = [1, 2, 3]; | ||
map(numbers); // => [1, 2, 3] | ||
|
||
// Using an object | ||
const obj = {a: 1, b: 2, c: 3}; | ||
map(obj, (value, key) => `${key}: ${value}`); // => ['a: 1', 'b: 2', 'c: 3'] | ||
``` |
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,77 @@ | ||
import { describe, expect, it } from 'vitest'; | ||
import { falsey } from '../_internal/falsey'; | ||
import { identity } from '../_internal/identity'; | ||
import { map } from '../index'; | ||
|
||
/** | ||
* @see https://github.com/lodash/lodash/blob/6a2cc1dfcf7634fea70d1bc5bd22db453df67b42/test/map.spec.js | ||
*/ | ||
describe('map', () => { | ||
const array = [1, 2]; | ||
|
||
it('should map values in `collection` to a new array', () => { | ||
const object = { a: 1, b: 2 }; | ||
const expected = ['1', '2']; | ||
|
||
expect(map(array, String)).toEqual(expected); | ||
expect(map(object, String)).toEqual(expected); | ||
}); | ||
|
||
it('should work with `_.property` shorthands', () => { | ||
const objects = [{ a: 'x' }, { a: 'y' }]; | ||
expect(map(objects, 'a')).toEqual(['x', 'y']); | ||
}); | ||
|
||
it('should iterate over own string keyed properties of objects', () => { | ||
class Foo { | ||
static b = 2; | ||
a: number; | ||
|
||
constructor() { | ||
this.a = 1; | ||
} | ||
|
||
[key: string]: unknown; | ||
} | ||
|
||
const actual = map(new Foo(), identity); | ||
expect(actual).toEqual([1]); | ||
}); | ||
|
||
it('should use `_.identity` when `iteratee` is nullish', () => { | ||
const object = { a: 1, b: 2 }; | ||
const values = [null, undefined]; | ||
const expected = values.map(() => [1, 2]); | ||
|
||
[array, object].forEach(collection => { | ||
const actual = values.map((value, index) => (index ? map(collection, value) : map(collection))); | ||
|
||
expect(actual).toEqual(expected); | ||
}); | ||
}); | ||
|
||
it('should accept a falsey `collection`', () => { | ||
const expected = falsey.map(() => []); | ||
|
||
const actual = (falsey as any[]).map((collection, index) => { | ||
try { | ||
return index ? map(collection) : map(null); | ||
} catch (e) { | ||
return e; | ||
} | ||
}); | ||
|
||
expect(actual).toEqual(expected); | ||
}); | ||
|
||
it('should treat number values for `collection` as empty', () => { | ||
expect(map(1)).toEqual([]); | ||
}); | ||
|
||
it('should work with objects with non-number length properties', () => { | ||
const value = { value: 'x' }; | ||
const object = { length: { value: 'x' } }; | ||
|
||
expect(map(object, identity)).toEqual([value]); | ||
}); | ||
}); |
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,60 @@ | ||
/** | ||
* Maps each element in a collection to a new array of values using an iteratee. | ||
* | ||
* This function takes a collection (either an array or an object) and applies | ||
* a mapping function (iteratee) to each element or value. It returns a new array | ||
* of values generated by the iteratee. If no iteratee is provided, the identity | ||
* function is used, meaning the values themselves are returned. | ||
* | ||
* @template T The type of elements or values in the collection. | ||
* @template U The type of elements in the resulting array. | ||
* @param {T[] | Record<string, T> | number | string | boolean | null | undefined} collection | ||
* The collection to iterate over. It can be an array, an object, or a primitive. | ||
* If the collection is null or undefined, an empty array is returned. | ||
* @param {((value: T, key: string | number) => U) | keyof T | null} [iteratee=null] | ||
* The function invoked per iteration. If a key of the collection elements is | ||
* provided instead of a function, the value of that key will be returned for | ||
* each element. | ||
* @returns {U[]} A new array of values mapped by the iteratee or by the identity function. | ||
* | ||
* @example | ||
* // Maps an array of numbers by multiplying each by 2 | ||
* map([1, 2, 3], n => n * 2); | ||
* // Returns: [2, 4, 6] | ||
* | ||
* @example | ||
* // Maps an object, extracting the values of the 'name' property | ||
* map({ a: { name: 'John' }, b: { name: 'Jane' } }, 'name'); | ||
* // Returns: ['John', 'Jane'] | ||
* | ||
* @example | ||
* // If the collection is null or undefined, returns an empty array | ||
* map(null); | ||
* // Returns: [] | ||
*/ | ||
export function map<T, U>( | ||
collection: T[] | Record<string, T> | number | string | boolean | null | undefined, | ||
iteratee?: ((value: T, key: string | number) => U) | keyof T | null | ||
): U[] { | ||
if (collection == null || typeof collection !== 'object') { | ||
return []; | ||
} | ||
|
||
const mapper: (value: T, key: string | number) => U = | ||
typeof iteratee === 'function' | ||
? iteratee | ||
: iteratee == null | ||
? (item: T) => item as unknown as U | ||
: (item: T) => item[iteratee as keyof T] as unknown as U; | ||
|
||
if (Array.isArray(collection)) { | ||
const length = collection.length; | ||
const result: U[] = new Array(length); | ||
for (let i = 0; i < length; i++) { | ||
result[i] = mapper(collection[i], i); | ||
} | ||
return result; | ||
} else { | ||
return Object.keys(collection).map(key => mapper(collection[key], key)); | ||
} | ||
} |
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
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe you might check our some, indexOf function which similarly handles this. I think the documentation and implementation is better.