feat(dropWhile): Add full compatibility with lodash

This commit is contained in:
Sojin Park 2024-09-28 22:15:16 +09:00
parent bdc3d976b3
commit c0efc65360
7 changed files with 275 additions and 32 deletions

View File

@ -27,3 +27,46 @@ const array = [1, 2, 3, 2, 4, 5];
const result = dropWhile(array, x => x < 3);
// 3未満の要素が見つかるまで要素を削除するので、結果は[3, 2, 4, 5]になります。
```
## Lodash 互換性
`es-toolkit/compat` から `dropWhile` をインポートすると、Lodash と互換になります。
配列から要素を削除する条件を、さまざまな方法で指定できます。
- **検査関数**: 各要素に対して検査する関数を実行します。最初に`false`を返す要素が見つかるまで要素を削除します。
- **部分オブジェクト**: 指定されたオブジェクトと部分的に一致しない要素が見つかるまで要素を削除します。
- **プロパティ-値ペア**: 指定されたプロパティと値が一致しない要素が見つかるまで要素を削除します。
- **プロパティ名**: 指定されたプロパティに対して偽と評価される値が見つかるまで要素を削除します。
### インターフェース
```typescript
function dropWhile<T>(arr: T[], canContinueDropping: (item: T, index: number, arr: T[]) => unknown): T[];
function dropWhile<T>(arr: T[], objectToDrop: Partial<T>): T[];
function dropWhile<T>(arr: T[], propertyToDrop: [keyof T, unknown]): T[];
function dropWhile<T>(arr: readonly T[], propertyToDrop: string): T[];
```
### 例
```typescript
// 検査関数を使用した例
const array1 = [1, 2, 3, 4, 5];
const result1 = dropWhile(array1, x => x < 3);
// result1 は [3, 4, 5] になります。3未満の要素が削除されるためです。
// 部分オブジェクトを使用した例
const array2 = [{ a: 1 }, { a: 2 }, { a: 3 }];
const result2 = dropWhile(array2, { a: 1 });
// result2 は [{ a: 2 }, { a: 3 }] になります。最初のオブジェクトが提供されたオブジェクトのプロパティと一致するためです。
// プロパティ-値ペアを使用した例
const array3 = [{ id: 1 }, { id: 2 }, { id: 3 }];
const result3 = dropWhile(array3, ['id', 1]);
// result3 は [{ id: 2 }, { id: 3 }] になります。最初のオブジェクトが id プロパティの値 1 と一致するためです。
// プロパティ名を使用した例
const array4 = [{ isActive: true }, { isActive: true }, { isActive: false }];
const result4 = dropWhile(array4, 'isActive');
// result4 は [{ isActive: false }] になります。偽の isActive プロパティを持つ要素が見つかるまで要素が削除されるためです。
```

View File

@ -27,3 +27,47 @@ const array = [1, 2, 3, 2, 4, 5];
const result = dropWhile(array, x => x < 3);
// 3보다 작은 요소를 발견할 때까지 요소를 제거하므로, 결괏값은 [3, 2, 4, 5]이 되어요.
```
## Lodash와 호환성
`es-toolkit/compat`에서 `dropWhile`를 가져오면 lodash와 완전히 호환돼요.
계속해서 배열에서 요소를 제거할 조건을 여러 방법들로 명시할 수 있어요.
- **검사 함수**: 각각의 요소에 대해서 검사하는 함수를 실행해요. 처음으로 `false`를 반환하게 하는 요소가 있을 때까지 요소를 제거해요.
- **부분 객체**: 주어진 객체와 부분적으로 일치하지 않는 요소가 있을 때까지 요소를 제거해요.
- **프로퍼티-값 쌍**: 해당 프로퍼티와 값이 일치하지 않는 요소가 있을 때까지 요소를 제거해요.
- **프로퍼티 이름**: 해당 프로퍼티에 대해서 거짓으로 평가되는 값이 있을 때까지 요소를 제거해요.
### 인터페이스
```typescript
function dropWhile<T>(arr: T[], canContinueDropping: (item: T, index: number, arr: T[]) => unknown): T[];
function dropWhile<T>(arr: T[], objectToDrop: Partial<T>): T[];
function dropWhile<T>(arr: T[], propertyToDrop: [keyof T, unknown]): T[];
function dropWhile<T>(arr: readonly T[], propertyToDrop: string): T[];
```
### 예시
```typescript
// 검사 함수를 사용하는 예시
const array1 = [1, 2, 3, 4, 5];
const result1 = dropWhile(array1, x => x < 3);
// 3보다 작은 요소가 제거되므로, 결과는 [3, 4, 5]가 돼요.
// 부분 객체를 사용하는 예시
const array2 = [{ a: 1 }, { a: 2 }, { a: 3 }];
const result2 = dropWhile(array2, { a: 1 });
// 첫 번째 요소만 부분 객체와 일치하므로, 결과는 [{ a: 2 }, { a: 3 }]가 돼요.
// 프로퍼티-값 쌍을 사용하는 예시
const array3 = [{ id: 1 }, { id: 2 }, { id: 3 }];
const result3 = dropWhile(array3, ['id', 1]);
// 첫 번째 요소만 `id` 프로퍼티가 `1`과 일치하므로, 결과는 [{ id: 2 }, { id: 3 }]가 돼요.
// 프로퍼티 이름을 사용하는 예시
const array4 = [{ isActive: true }, { isActive: true }, { isActive: false }];
const result4 = dropWhile(array4, 'isActive');
// `isActive`가 참으로 평가되는 요소들이 있을 때까지 요소를 제거하므로, 결과는 [{ isActive: false }]이 돼요.
```

View File

@ -8,13 +8,13 @@ predicate function returns `false`. It then returns a new array with the remaini
## Signature
```typescript
function dropWhile<T>(arr: T[], canContinueDropping: (item: T) => boolean): T[];
function dropWhile<T>(arr: T[], canContinueDropping: (item: T, index: number, arr: T[]) => boolean): T[];
```
### Parameters
- `arr` (`T[]`): The array from which to drop elements.
- `canContinueDropping` (`(item: T) => boolean`): A predicate function that determines whether to continue dropping elements. The function is called with each element, and dropping continues as long as it returns `true`.
- `canContinueDropping` (`(item: T, index: number, arr: T[]) => boolean`): A predicate function that determines whether to continue dropping elements. The function is called with each element, and dropping continues as long as it returns `true`.
### Returns
@ -27,3 +27,47 @@ const array = [1, 2, 3, 4, 5];
const result = dropWhile(array, x => x < 3);
// result will be [3, 4, 5] since elements less than 3 are dropped.
```
## Compatibility with Lodash
Import `dropWhile` from `es-toolkit/compat` for full compatibility with lodash.
You can specify the condition for dropping elements, and the array will remove items from the beginning as long as that condition evaluates to true.
- **Predicate function**: You can provide a predicate function that will be applied to each item in the array. The function should return `true` for elements that should be dropped. Dropping continues until the predicate returns `false` for the first time.
- **Partial object**: You can also provide a partial object, and the function will drop elements from the array as long as their properties match the properties of the provided object.
- **Property-value pair**: Alternatively, you can specify a property-value pair, where the function will drop elements that have the specified property matching the given value.
- **Property name**: Lastly, you can provide a property name, and the function will drop elements from the array until it finds an element where the specified property has a truthy value.
### Signature
```typescript
function dropWhile<T>(arr: T[], canContinueDropping: (item: T, index: number, arr: T[]) => unknown): T[];
function dropWhile<T>(arr: T[], objectToDrop: Partial<T>): T[];
function dropWhile<T>(arr: T[], propertyToDrop: [keyof T, unknown]): T[];
function dropWhile<T>(arr: readonly T[], propertyToDrop: string): T[];
```
### Examples
```typescript
// Example using a predicate function
const array1 = [1, 2, 3, 4, 5];
const result1 = dropWhile(array1, x => x < 3);
// result1 will be [3, 4, 5] since elements less than 3 are dropped.
// Example using a partial object
const array2 = [{ a: 1 }, { a: 2 }, { a: 3 }];
const result2 = dropWhile(array2, { a: 1 });
// result2 will be [{ a: 2 }, { a: 3 }] since the first object matches the properties of the provided object.
// Example using a property-value pair
const array3 = [{ id: 1 }, { id: 2 }, { id: 3 }];
const result3 = dropWhile(array3, ['id', 1]);
// result3 will be [{ id: 2 }, { id: 3 }] since the first object has the id property matching the value 1.
// Example using a property name
const array4 = [{ isActive: true }, { isActive: true }, { isActive: false }];
const result4 = dropWhile(array4, 'isActive');
// result4 will be [{ isActive: false }] since it drops elements until it finds one with a falsy isActive property.
```

View File

@ -26,3 +26,47 @@ const array = [1, 2, 3, 4, 5];
const result = dropWhile(array, x => x < 3);
// 结果将是 [3, 4, 5],因为小于 3 的元素从开头被移除了。
```
## Lodash 兼容性
`es-toolkit/compat` 导入 `dropWhile` 以实现与 lodash 的完全兼容。
您可以指定移除元素的条件,只要该条件评估为 true数组将从开头移除项目。
- **谓词函数**:您可以提供一个谓词函数,该函数将应用于数组中的每个项目。该函数应返回 `true`,以指示应移除的元素。移除操作将持续进行,直到谓词第一次返回 `false`
- **部分对象**:您还可以提供一个部分对象,只要其属性与提供的对象的属性匹配,函数就会从数组中移除元素。
- **属性-值对**:或者,您可以指定一个属性-值对,函数将移除具有指定属性且与给定值匹配的元素。
- **属性名**:最后,您可以提供一个属性名,函数将从数组中移除元素,直到找到一个指定属性为真值的元素。
### 签名
```typescript
function dropWhile<T>(arr: T[], canContinueDropping: (item: T, index: number, arr: T[]) => unknown): T[];
function dropWhile<T>(arr: T[], objectToDrop: Partial<T>): T[];
function dropWhile<T>(arr: T[], propertyToDrop: [keyof T, unknown]): T[];
function dropWhile<T>(arr: readonly T[], propertyToDrop: string): T[];
```
### 示例
```typescript
// 使用谓词函数的示例
const array1 = [1, 2, 3, 4, 5];
const result1 = dropWhile(array1, x => x < 3);
// result1 将是 [3, 4, 5],因为小于 3 的元素被移除了。
// 使用部分对象的示例
const array2 = [{ a: 1 }, { a: 2 }, { a: 3 }];
const result2 = dropWhile(array2, { a: 1 });
// result2 将是 [{ a: 2 }, { a: 3 }],因为第一个对象与提供的对象的属性匹配。
// 使用属性-值对的示例
const array3 = [{ id: 1 }, { id: 2 }, { id: 3 }];
const result3 = dropWhile(array3, ['id', 1]);
// result3 将是 [{ id: 2 }, { id: 3 }],因为第一个对象的 id 属性与值 1 匹配。
// 使用属性名的示例
const array4 = [{ isActive: true }, { isActive: true }, { isActive: false }];
const result4 = dropWhile(array4, 'isActive');
// result4 将是 [{ isActive: false }],因为它会移除元素,直到找到一个 isActive 属性为假值的元素。
```

View File

@ -16,8 +16,12 @@
* const result = dropWhile(array, x => x < 3);
* // result will be [3, 4, 5] since elements less than 3 are dropped.
*/
export function dropWhile<T>(arr: readonly T[], canContinueDropping: (item: T) => boolean): T[] {
const dropEndIndex = arr.findIndex(item => !canContinueDropping(item));
export function dropWhile<T>(
arr: readonly T[],
canContinueDropping: (item: T, index: number, arr: readonly T[]) => boolean
): T[] {
const dropEndIndex = arr.findIndex((item, index, arr) => !canContinueDropping(item, index, arr));
if (dropEndIndex === -1) {
return [];
}

View File

@ -1,6 +1,8 @@
import { describe, expect, it } from 'vitest';
import { dropWhile } from './dropWhile';
import { slice } from '../_internal/slice';
import { range } from '../../math/range';
import { LARGE_ARRAY_SIZE } from '../_internal/LARGE_ARRAY_SIZE';
/**
* @see https://github.com/lodash/lodash/blob/6a2cc1dfcf7634fea70d1bc5bd22db453df67b42/test/dropWhile.spec.js
@ -8,19 +10,21 @@ import { slice } from '../_internal/slice';
describe('dropWhile', () => {
const array = [1, 2, 3, 4];
// const objects = [
// { a: 2, b: 2 },
// { a: 1, b: 1 },
// { a: 0, b: 0 },
// ];
const objects = [
{ a: 2, b: 2 },
{ a: 1, b: 1 },
{ a: 0, b: 0 },
];
it('should drop elements while `predicate` returns truthy', () => {
const actual = dropWhile(array, n => n < 3);
it('should drop elements while `predicate` returns truthy', function () {
const actual = dropWhile(array, function (n) {
return n < 3;
});
expect(actual).toEqual([3, 4]);
});
it('should provide correct `predicate` arguments', () => {
it('should provide correct `predicate` arguments', function () {
let args;
dropWhile(array, function () {
@ -30,17 +34,15 @@ describe('dropWhile', () => {
expect(args).toEqual([1, 0, array]);
});
// FIXME: Perhaps dropWhile semantic changes.
//
// it('should work with `_.matches` shorthands', () => {
// expect(dropWhile(objects, { b: 2 })).toEqual(objects.slice(1));
// });
//
// it('should work with `_.matchesProperty` shorthands', () => {
// expect(dropWhile(objects, ['b', 2])).toEqual(objects.slice(1));
// });
//
// it('should work with `_.property` shorthands', () => {
// expect(dropWhile(objects, 'b')).toEqual(objects.slice(2));
// });
it('should work with `_.matches` shorthands', function () {
expect(dropWhile(objects, { b: 2 })).toEqual(objects.slice(1));
});
it('should work with `_.matchesProperty` shorthands', function () {
expect(dropWhile(objects, ['b', 2])).toEqual(objects.slice(1));
});
it('should work with `_.property` shorthands', function () {
expect(dropWhile(objects, 'b')).toEqual(objects.slice(2));
});
});

View File

@ -1,3 +1,53 @@
import { dropWhile as dropWhileToolkit } from '../../array/dropWhile.ts';
import { property } from '../object/property.ts';
import { matches } from '../predicate/matches.ts';
import { matchesProperty } from '../predicate/matchesProperty.ts';
/**
* Drops elements from the beginning of an array while the predicate function returns truthy.
*
* @template T - The type of elements in the array.
* @param {T[]} arr - The array from which to drop elements.
* @param {(item: T, index: number, arr: T[]) => unknown} canContinueDropping - A predicate function that determines
* whether to continue dropping elements. The function is called with each element, index, and array, and dropping
* continues as long as it returns true.
* @returns {T[]} A new array with the elements remaining after the predicate returns false.
*/
export function dropWhile<T>(
arr: readonly T[],
canContinueDropping: (item: T, index: number, arr: readonly T[]) => unknown
): T[];
/**
* Drops elements from the beginning of an array while the specified object properties match.
*
* @template T - The type of elements in the array.
* @param {T[]} arr - The array from which to drop elements.
* @param {Partial<T>} objectToDrop - An object specifying the properties to match for dropping elements.
* @returns {T[]} A new array with the elements remaining after the predicate returns false.
*/
export function dropWhile<T>(arr: readonly T[], objectToDrop: Partial<T>): T[];
/**
* Drops elements from the beginning of an array while the specified property matches a given value.
*
* @template T - The type of elements in the array.
* @param {T[]} arr - The array from which to drop elements.
* @param {[keyof T, unknown]} propertyToDrop - A tuple containing the property key and the value to match for dropping elements.
* @returns {T[]} A new array with the elements remaining after the predicate returns false.
*/
export function dropWhile<T>(arr: readonly T[], propertyToDrop: [keyof T, unknown]): T[];
/**
* Drops elements from the beginning of an array while the specified property name matches.
*
* @template T - The type of elements in the array.
* @param {T[]} arr - The array from which to drop elements.
* @param {string} propertyToDrop - The name of the property to match for dropping elements.
* @returns {T[]} A new array with the elements remaining after the predicate returns false.
*/
export function dropWhile<T>(arr: readonly T[], propertyToDrop: string): T[];
/**
* Removes elements from the beginning of an array until the predicate returns false.
*
@ -6,7 +56,7 @@
*
* @template T - The type of elements in the array.
* @param {T[]} arr - The array from which to drop elements.
* @param {(item: T, index: number, arr: T[]) => unknown} canContinueDropping - A predicate function that determines
* @param {(item: T, index: number, arr: T[]) => unknown} predicate - A predicate function that determines
* whether to continue dropping elements. The function is called with each element, index, and array, and dropping
* continues as long as it returns true.
* @returns {T[]} A new array with the elements remaining after the predicate returns false.
@ -18,12 +68,24 @@
*/
export function dropWhile<T>(
arr: readonly T[],
canContinueDropping: (item: T, index: number, arr: readonly T[]) => unknown
predicate: ((item: T, index: number, arr: readonly T[]) => unknown) | Partial<T> | [keyof T, unknown] | string
): T[] {
const dropEndIndex = arr.findIndex((item, index, arr) => !canContinueDropping(item, index, arr));
if (dropEndIndex === -1) {
return [];
}
switch (typeof predicate) {
case 'function': {
return dropWhileToolkit(arr, (item, index, arr) => Boolean(predicate(item, index, arr)));
}
case 'object': {
if (Array.isArray(predicate) && predicate.length === 2) {
const key = predicate[0];
const value = predicate[1];
return arr.slice(dropEndIndex);
return dropWhileToolkit(arr, matchesProperty(key, value));
} else {
return dropWhileToolkit(arr, matches(predicate));
}
}
case 'string': {
return dropWhileToolkit(arr, property(predicate));
}
}
}