feat(filter): add filter function (#610)

* feat: create filter function

* test: filter

* docs: filter

* fix: test case description

---------

Co-authored-by: Sojin Park <raon0211@toss.im>
This commit is contained in:
kimjeonghee 2024-09-28 23:44:52 +09:00 committed by GitHub
parent 5553908173
commit 6e3226e2f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 831 additions and 0 deletions

View File

@ -0,0 +1,27 @@
import { filter as filterToolkit } from 'es-toolkit/compat';
import { filter as filterLodash } from 'lodash';
import { bench, describe } from 'vitest';
const arr = [
{ a: 0, b: true },
{ a: 1, b: true },
{ a: 0, b: false },
];
describe('filter', () => {
bench('es-toolkit/filter', () => {
filterToolkit([1, 2, 3], number => number % 2 === 0);
filterToolkit(arr, { b: true });
filterToolkit(arr, ['a', 1]);
filterToolkit(arr, items => items.b);
filterToolkit({ a: 1, b: 2, c: 3 }, 'b');
});
bench('lodash/filter', () => {
filterLodash([1, 2, 3], number => number % 2 === 0);
filterLodash(arr, { b: true });
filterLodash(arr, ['a', 1]);
filterLodash(arr, items => items.b);
filterLodash({ a: 1, b: 2, c: 3 }, 'b');
});
});

View File

@ -0,0 +1,120 @@
# filter
::: info
この関数は互換性のために `es-toolkit/compat` からのみインポートできます。代替可能なネイティブ JavaScript API があるか、まだ十分に最適化されていないためです。
`es-toolkit/compat` からこの関数をインポートすると、[lodash と完全に同じように動作](../../../compatibility.md)します。
:::
与えられた条件を満たす要素を持つ新しい配列を返します。
条件はいくつかの方法で指定できます。
- **検査関数**: 各要素に対して検査する関数を実行します。条件に合う要素を選択します。
- **部分オブジェクト**: 指定されたオブジェクトと部分的に一致する要素を選択します。
- **プロパティ-値ペア**: 指定されたプロパティに対してキーと値が一致する要素を選択します。
- **プロパティ名**: 指定されたプロパティ名が存在する要素を選択します。
## インターフェース
```typescript
function filter<T>(arr: T[], doesMatch: (item: T, index: number, arr: T[]) => unknown): T[];
function filter<T>(arr: T[], doesMatch: Partial<T>): T[];
function filter<T>(arr: T[], doesMatch: [keyof T, unknown]): T[];
function filter<T>(arr: T[], doesMatch: string): T[];
function filter<T extends Record<string, unknown>>(
object: T,
doesMatch: (item: T[keyof T], index: number, object: T) => unknown
): T[];
function filter<T extends Record<string, unknown>>(object: T, doesMatch: Partial<T[keyof T]>): T[];
function filter<T extends Record<string, unknown>>(object: T, doesMatch: [keyof T, unknown]): T[];
function filter<T extends Record<string, unknown>>(object: T, doesMatch: string): T[];
```
### パラメータ
- `arr` (`T[]`) または `object` (`T`): 繰り返し処理する配列やオブジェクト。
- `doesMatch`:
- 配列の場合:
- **検査関数** (`(item: T, index: number, arr: T[]) => unknown`): 各要素が条件を満たしているか確認する関数。
- **部分オブジェクト** (`Partial<T>`): 要素の属性と値が一致するか確認する部分オブジェクト。
- **プロパティ-値ペア** (`[keyof T, unknown]`): 最初の要素が対象プロパティ、2番目が対象値を示すタプル。
- **プロパティ名** (`string`): 特定の属性を持っているか確認するプロパティ名。
- オブジェクトの場合:
- **検査関数** (`(item: T[keyof T], index: number, object: T) => unknown`): 各要素が条件を満たしているか確認する関数。
- **部分値** (`Partial<T[keyof T]>`): 要素の属性と値が一致するか確認する部分オブジェクト。
- **プロパティ-値ペア** (`[keyof T, unknown]`): 最初の要素が対象プロパティ、2番目が対象値を示すタプル。
- **プロパティ名** (`string`): 特定の属性を持っているか確認するプロパティ名。
### 戻り値
(`T[]`): 条件を満たす要素の配列。該当する要素がなければ (`[]`)
## 例
### 配列の場合
```typescript
import { find } from 'es-toolkit/compat';
// 検査関数を使う場合
const items = [1, 2, 3, 4, 5];
const result = find(items, item => item > 3);
console.log(result); // 4
// 部分オブジェクトを使う場合
const items = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
const result = find(items, { name: 'Bob' });
console.log(result); // { id: 2, name: 'Bob' }
// プロパティ-値ペアを使う場合
const items = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
const result = find(items, ['name', 'Alice']);
console.log(result); // { id: 1, name: 'Alice' }
// プロパティ名を使う場合
const items = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
const result = find(items, 'name');
console.log(result); // { id: 1, name: 'Alice' }
```
### オブジェクトの場合
```typescript
import { find } from 'es-toolkit/compat';
// 検査関数を使う場合
const obj = { a: 1, b: 2, c: 3 };
const result = find(obj, item => item > 2);
console.log(result); // 3
// 部分オブジェクトを使う場合
const obj = { a: { id: 1, name: 'Alice' }, b: { id: 2, name: 'Bob' } };
const result = find(obj, { name: 'Bob' });
console.log(result); // { id: 2, name: 'Bob' }
// プロパティ-値ペアを使う場合
const items = { alice: { id: 1, name: 'Alice' }, bob: { id: 2, name: 'Bob' } };
const result = find(items, ['name', 'Alice']);
console.log(result); // { id: 1, name: 'Alice' }
// プロパティ名を使う場合
const obj = { a: { id: 1, name: 'Alice' }, b: { id: 2, name: 'Bob' } };
const result = find(obj, 'name');
console.log(result); // { id: 1, name: 'Alice' }
```

View File

@ -0,0 +1,130 @@
# filter
::: info
이 함수는 호환성을 위한 `es-toolkit/compat` 에서만 가져올 수 있어요. 대체할 수 있는 네이티브 JavaScript API가 있거나, 아직 충분히 최적화되지 않았기 때문이에요.
`es-toolkit/compat`에서 이 함수를 가져오면, [lodash와 완전히 똑같이 동작](../../../compatibility.md)해요.
:::
주어진 조건을 만족하는 요소를 갖는 새로운 배열을 반환해요.
조건은 여러 방법들로 명시할 수 있어요.
- **검사 함수**: 각각의 요소에 대해서 검사하는 함수를 실행해요. 조건에 맞는 요소들을 선택해요.
- **부분 객체**: 주어진 객체와 부분적으로 일치하는 요소들을 선택해요.
- **프로퍼티-값 쌍**: 해당 프로퍼티에 대해서 키와 값이 일치하는 요소들을 선택해요.
- **프로퍼티 이름**: 해당 프로퍼티 이름이 일치하는 요소들을 선택해요.
## 인터페이스
```typescript
function filter<T>(arr: T[], doesMatch: (item: T, index: number, arr: T[]) => unknown): T[];
function filter<T>(arr: T[], doesMatch: Partial<T>): T[];
function filter<T>(arr: T[], doesMatch: [keyof T, unknown]): T[];
function filter<T>(arr: T[], doesMatch: string): T[];
function filter<T extends Record<string, unknown>>(
object: T,
doesMatch: (item: T[keyof T], index: number, object: T) => unknown
): T[];
function filter<T extends Record<string, unknown>>(object: T, doesMatch: Partial<T[keyof T]>): T[];
function filter<T extends Record<string, unknown>>(object: T, doesMatch: [keyof T, unknown]): T[];
function filter<T extends Record<string, unknown>>(object: T, doesMatch: string): T[];
```
### 파라미터
- `arr` (`T[]`) or `object` (`T`): 반복할 배열이나 객체.
- `doesMatch`:
- 배열의 경우:
- **검사 함수** (`(item: T, index: number, arr: T[]) => unknown`): 각 요소가 조건을 만족하는지 확인하는 함수.
- **부분 객체** (`Partial<T>`): 요소의 속성과 값과 일치하는지 확인할 부분 객체.
- **프로퍼티-값 쌍** (`[keyof T, unknown]`): 첫 번째가 찾는 프로퍼티, 두 번째가 찾는 값을 나타내는 튜플.
- **프로퍼티 이름** (`string`): 특정 속성을 가지고 있는지 확인할 프로퍼티 이름.
- 객체의 경우:
- **검사 함수** (`(item: T[keyof T], index: number, object: T) => unknown`): 각 요소가 조건을 만족하는지 확인하는 함수.
- **부분 객체** (`Partial<T[keyof T]>`): 요소의 속성과 값과 일치하는지 확인할 부분 객체.
- **프로퍼티-값 쌍** (`[keyof T, unknown]`): 첫 번째가 찾는 프로퍼티, 두 번째가 찾는 값을 나타내는 튜플.
- **프로퍼티 이름** (`string`): 특정 속성을 가지고 있는지 확인할 프로퍼티 이름.
### 반환 값
(`T[]`): 조건을 만족하는 요소 배열. 없으면 (`[]`)
## 예시
### 배열의 경우
```typescript
import { filter } from 'es-toolkit/compat';
// 검사 함수를 쓰는 경우
filter([1, 2, 3], n => n % 2 === 0);
// => [2]
// 부분 객체를 쓰는 경우
const arr = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
filter(arr, { name: 'Bob' });
// => [{ id: 2, name: 'Bob' }]
// 프로퍼티-값 쌍을 쓰는 경우
const arr = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
filter(arr, ['name', 'Alice']);
// => [{ id: 1, name: 'Alice' }]
// 프로퍼티 이름을 쓰는 경우
const arr = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, age: 28 },
];
filter(arr, 'name');
// => [{ id: 1, name: 'Alice' }]
```
### 객체의 경우
```typescript
import { filter } from 'es-toolkit/compat';
// 검사 함수를 쓰는 경우
const obj = { a: 1, b: 2, c: 3 };
filter(obj, item => item > 2);
// => [3]
// 부분 객체를 쓰는 경우
const obj = {
a: { id: 1, name: 'Alice' },
b: { id: 2, name: 'Bob' },
};
filter(obj, { name: 'Bob' });
// => [{ id: 2, name: 'Bob' }]
// 프로퍼티-값 쌍을 쓰는 경우
const obj = {
alice: { id: 1, name: 'Alice' },
bob: { id: 2, name: 'Bob' },
};
filter(obj, ['name', 'Alice']);
// => [{ id: 1, name: 'Alice' }]
// 프로퍼티 이름을 쓰는 경우
const obj = {
a: { id: 1, name: 'Alice' },
b: { id: 2, name: 'Bob' },
c: { id: 3, age: 28 },
};
filter(obj, 'name');
// => [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
```

View File

@ -0,0 +1,129 @@
# filter
::: info
This function is only available in `es-toolkit/compat` for compatibility reasons. It either has alternative native JavaScript APIs or isnt fully optimized yet.
When imported from `es-toolkit/compat`, it behaves exactly like lodash and provides the same functionalities, as detailed [here](../../../compatibility.md).
:::
Returns a new array of elements that satisfy the provided condition.
The condition can be specified in several ways:
- **Predicate function**: Runs a function for each element, selecting elements that satisfy the condition.
- **Partial object**: Selects elements that partially match the provided object.
- **Property-value pair**: Selects elements where a specified key matches a given value.
- **Property name**: Selects elements where the specified property name exists.
## Signature
```typescript
function filter<T>(arr: T[], doesMatch: (item: T, index: number, arr: T[]) => unknown): T[];
function filter<T>(arr: T[], doesMatch: Partial<T>): T[];
function filter<T>(arr: T[], doesMatch: [keyof T, unknown]): T[];
function filter<T>(arr: T[], doesMatch: string): T[];
function filter<T extends Record<string, unknown>>(
object: T,
doesMatch: (item: T[keyof T], index: number, object: T) => unknown
): T[];
function filter<T extends Record<string, unknown>>(object: T, doesMatch: Partial<T[keyof T]>): T[];
function filter<T extends Record<string, unknown>>(object: T, doesMatch: [keyof T, unknown]): T[];
function filter<T extends Record<string, unknown>>(object: T, doesMatch: string): T[];
```
### Parameters
- `arr` (`T[]`) or `object` (`T`): The array or object to iterate over.
- `doesMatch`:
- For the first `filter` overload with arrays:
- **Predicate function** (`(item: T, index: number, arr: T[]) => unknown`): A function to check if an element satisfies a condition.
- **Partial object** (`Partial<T>`): A partial object that specifies the properties to match.
- **Property-value pair** (`[keyof T, unknown]`): An array where the first element is the property key and the second element is the value to match.
- **Property name** (`string`): The name of the property to check for in the elements.
- For the `filter` overloads with objects:
- **Predicate function** (`(item: T[keyof T], index: number, object: T) => unknown`): A function that takes an item, its key, and the object, and returns a truthy value if the item matches the criteria.
- **Partial value** (`Partial<T[keyof T]>`): A partial value to match against the values of the object.
- **Property-value pair** (`[keyof T, unknown]`): An array where the first element is the property key and the second element is the value to match.
- **Property name** (`string`): The name of the property to check for a truthy value.
### Returns
(`T[]`): An array of elements that satisfy the condition. If none, an empty array. (`[]`)
## Examples
### Array
```typescript
import { filter } from 'es-toolkit/compat';
// Using a predicate function
filter([1, 2, 3], n => n % 2 === 0);
// => [2]
// Using a partial object
const arr = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
filter(arr, { name: 'Bob' });
// => [{ id: 2, name: 'Bob' }]
// Using a property-value pair
const arr = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
filter(arr, ['name', 'Alice']);
// => [{ id: 1, name: 'Alice' }]
// Using a property name
const arr = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, age: 28 },
];
filter(arr, 'name');
// => [{ id: 1, name: 'Alice' }]
```
### Object
```typescript
import { filter } from 'es-toolkit/compat';
// Using a predicate function
const obj = { a: 1, b: 2, c: 3 };
filter(obj, item => item > 2);
// => [3]
// Using a partial object
const obj = {
a: { id: 1, name: 'Alice' },
b: { id: 2, name: 'Bob' },
};
filter(obj, { name: 'Bob' });
// => [{ id: 2, name: 'Bob' }]
// Using a property-value pair
const obj = {
alice: { id: 1, name: 'Alice' },
bob: { id: 2, name: 'Bob' },
};
filter(obj, ['name', 'Alice']);
// => [{ id: 1, name: 'Alice' }]
// Using a property name
const obj = {
a: { id: 1, name: 'Alice' },
b: { id: 2, name: 'Bob' },
c: { id: 3, age: 28 },
};
filter(obj, 'name');
// => [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
```

View File

@ -0,0 +1,130 @@
# filter
::: info
出于兼容性原因,此函数仅在 `es-toolkit/compat` 中提供。它可能具有替代的原生 JavaScript API或者尚未完全优化。
`es-toolkit/compat` 导入时,它的行为与 lodash 完全一致,并提供相同的功能,详情请见 [这里](../../../compatibility.md)。
:::
返回满足提供条件的元素新数组。
条件可以通过多种方式指定:
- **谓词函数**: 对每个元素执行一个函数,选择满足条件的元素。
- **部分对象**: 选择与提供的对象部分匹配的元素。
- **属性-值对**: 选择指定键与给定值匹配的元素。
- **属性名称**: 选择存在指定属性名的元素。
## 签名
```typescript
function filter<T>(arr: T[], doesMatch: (item: T, index: number, arr: T[]) => unknown): T[];
function filter<T>(arr: T[], doesMatch: Partial<T>): T[];
function filter<T>(arr: T[], doesMatch: [keyof T, unknown]): T[];
function filter<T>(arr: T[], doesMatch: string): T[];
function filter<T extends Record<string, unknown>>(
object: T,
doesMatch: (item: T[keyof T], index: number, object: T) => unknown
): T[];
function filter<T extends Record<string, unknown>>(object: T, doesMatch: Partial<T[keyof T]>): T[];
function filter<T extends Record<string, unknown>>(object: T, doesMatch: [keyof T, unknown]): T[];
function filter<T extends Record<string, unknown>>(object: T, doesMatch: string): T[];
```
### 参数
- `arr` (`T[]`) 或 `object` (`T`): 要迭代的数组或对象。
- `doesMatch`:
- 对于数组的第一个 `filter` 重载:
- **谓词函数** (`(item: T, index: number, arr: T[]) => unknown`): 一个检查元素是否满足条件的函数。
- **部分对象** (`Partial<T>`): 用于检查元素的属性和值是否匹配的部分对象。
- **属性-值对** (`[keyof T, unknown]`): 一个数组,第一个元素是属性键,第二个元素是要匹配的值。
- **属性名称** (`string`): 用于检查是否具有特定属性的属性名称。
- 对于对象的 `filter` 重载:
- **谓词函数** (`(item: T[keyof T], index: number, object: T) => unknown`): 一个函数,接收一个项、其键和对象,如果该项符合条件,则返回一个真值。
- **部分值** (`Partial<T[keyof T]>`): 用于检查元素的属性和值是否匹配的部分对象。
- **属性-值对** (`[keyof T, unknown]`): 一个数组,第一个元素是属性键,第二个元素是要匹配的值。
- **属性名称** (`string`): 用于检查是否具有特定属性的属性名称。
### 返回
(`T[]`): 满足条件的元素数组。如果没有,返回空数组。 (`[]`)
## 示例
### 数组
```typescript
import { filter } from 'es-toolkit/compat';
// 使用谓词函数
filter([1, 2, 3], n => n % 2 === 0);
// => [2]
// 使用部分对象
const arr = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
filter(arr, { name: 'Bob' });
// => [{ id: 2, name: 'Bob' }]
// 使用属性-值对
const arr = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
filter(arr, ['name', 'Alice']);
// => [{ id: 1, name: 'Alice' }]
// 使用属性名称
const arr = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, age: 28 },
];
filter(arr, 'name');
// => [{ id: 1, name: 'Alice' }]
```
### 对象
```typescript
import { filter } from 'es-toolkit/compat';
// 使用谓词函数
const obj = { a: 1, b: 2, c: 3 };
filter(obj, item => item > 2);
// => [3]
// 使用部分对象
const obj = {
a: { id: 1, name: 'Alice' },
b: { id: 2, name: 'Bob' },
};
filter(obj, { name: 'Bob' });
// => [{ id: 2, name: 'Bob' }]
// 使用属性-值对
const obj = {
alice: { id: 1, name: 'Alice' },
bob: { id: 2, name: 'Bob' },
};
filter(obj, ['name', 'Alice']);
// => [{ id: 1, name: 'Alice' }]
// 使用属性名称
const obj = {
a: { id: 1, name: 'Alice' },
b: { id: 2, name: 'Bob' },
c: { id: 3, age: 28 },
};
filter(obj, 'name');
// => [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
```

View File

@ -0,0 +1,115 @@
import { describe, expect, it } from 'vitest';
import { filter } from './filter';
function isEven(n: number) {
return n % 2 === 0;
}
describe('filter', () => {
it('It should return an empty array when no predicate is provided.', () => {
const arr = [1, 2, 3];
expect(filter(arr)).toEqual([1, 2, 3]);
});
it('should return an array of elements from the array', () => {
const arr = [1, 2, 3];
expect(filter(arr, isEven)).toEqual([2]);
});
it(`filter should work with \`matches\` shorthands`, () => {
const arr = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
expect(filter(arr, { name: 'Bob' })).toEqual([{ id: 2, name: 'Bob' }]);
});
it(`filter should work with \`matchesProperty\` shorthands`, () => {
const arr = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
expect(filter(arr, ['name', 'Alice'])).toEqual([{ id: 1, name: 'Alice' }]);
});
it(`filter should work with \`property\` shorthands`, () => {
const arr = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, age: 28 },
];
expect(filter(arr, 'name')).toEqual([
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
]);
});
it(`should return an array of elements from the object`, () => {
const obj: { [key: string]: number } = {
a: 1,
b: 2,
c: 3,
};
expect(filter(obj, item => item > 2)).toEqual([3]);
});
it(`filter should work with \`matches\` shorthands for objects.`, () => {
const obj = {
item1: { a: 0 },
item2: { a: 1 },
item3: { a: 2 },
};
expect(filter(obj, { a: 1 })).toEqual([{ a: 1 }]);
});
it(`filter should work with \`matches\` shorthands for nested objects.`, () => {
const obj = {
item1: { a: 0, b: { c: 1 } },
item2: { a: 1, b: { c: 2 } },
item3: { a: 0, b: { c: 1 } },
};
expect(filter(obj, { b: { c: 1 } })).toEqual([
{ a: 0, b: { c: 1 } },
{ a: 0, b: { c: 1 } },
]);
});
it(`filter should work when looking for values inside nested objects`, () => {
const obj: Record<string, unknown> = {
item1: { a: 0, b: { c: 1 } },
item2: { a: 1, b: { c: 2 } },
item3: { a: 0, b: { c: 1 } },
};
expect(filter(obj, ['b.c', 2])).toEqual([{ a: 1, b: { c: 2 } }]);
});
it('filter should match nested object properties using key-value pair shorthand.', () => {
const obj: { [key: string]: { [key: string]: unknown } } = {
alice: { id: 1, name: 'Alice' },
bob: { id: 2, name: 'Bob' },
};
expect(filter(obj, ['name', 'Alice'])).toEqual([{ id: 1, name: 'Alice' }]);
});
it('filter should return objects that have the specified key.', () => {
const obj: { [key: string]: { [key: string]: unknown } } = {
a: { id: 1, name: 'Alice' },
b: { id: 2, name: 'Bob' },
c: { id: 3, age: 28 },
};
expect(filter(obj, 'name')).toEqual([
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
]);
});
});

179
src/compat/array/filter.ts Normal file
View File

@ -0,0 +1,179 @@
import { identity } from '../_internal/identity';
import { property } from '../object/property';
import { isArray } from '../predicate/isArray';
import { matches } from '../predicate/matches';
import { matchesProperty } from '../predicate/matchesProperty';
/**
* Filters items from a array and returns an array of elements.
*
* @template T
* @param {T[]} arr - The array to iterate over.
* @param {(item: T, index: number, arr: T[]) => unknown} doesMatch - The function invoked per iteration.
* @returns {T[]} - Returns a new array of elements that satisfy the given doesMatch function.
*
* @example
* filter([1, 2, 3], n => n % 2 === 0)
* // => [2]
*/
export function filter<T>(arr: readonly T[], doesMatch?: (item: T, index: number, arr: readonly T[]) => unknown): T[];
/**
* Filters elements in a arr that match the properties of the given partial object.
*
* @template T
* @param {T[]} arr - The array to iterate over.
* @param {Partial<T>} doesMatch - A partial object that specifies the properties to match.
* @returns {T[]} - Returns a new array of elements that match the given properties.
*
* @example
* const arr = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
* filter(arr, { name: 'Bob' });
* // => [{ id: 2, name: 'Bob' }]
*/
export function filter<T>(arr: readonly T[], doesMatch: Partial<T>): T[];
/**
* Filters elements in a arr that match the given key-value pair.
*
* @template T
* @param {readonly T[]} arr - The array to iterate over.
* @param {[keyof T, unknown]} doesMatchProperty - The key-value pair to match.
* @returns {T[]} - Returns a new array of elements that match the given key-value pair.
*
* @example
* const arr = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
* filter(arr, ['name', 'Alice']);
* // => [{ id: 1, name: 'Alice' }]
*/
export function filter<T>(arr: readonly T[], doesMatchProperty: [keyof T, unknown]): T[];
/**
* Filters the arr, returning elements that contain the given property name.
*
* @template T
* @param {readonly T[]} arr - The array to iterate over.
* @param {string} propertyToCheck - The property name to check.
* @returns {T[]} - Returns a new array of elements that match the given property name.
*
* @example
* const arr = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, { id: 3, age: 28 }];
* filter(arr, 'name');
* // => [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
*/
export function filter<T>(arr: readonly T[], propertyToCheck: string): T[];
/**
* Filters items from a object and returns an array of elements that match the given predicate function.
*
* @template T
* @param {T extends Record<string, unknown> ? T : never} object - The object to iterate over.
* @param {(item: T[keyof T], index: number, arr: T) => unknown} doesMatch - The function invoked per iteration.
* @returns {T[]} - Returns a new array of elements that satisfy the given predicate function.
*
* @example
* const obj = { item1: { a: 0 }, item2: { a: 1 }, item3: { a: 0 } }
* filter(obj, items => items.a)
* // => [{ a: 1 }]
*
* const obj = { a: 1, b: 2, c: 3 };
* filter(obj, item => item > 2)
* // => [3]
*/
export function filter<T extends Record<string, unknown>>(
object: T,
doesMatch: (item: T[keyof T], index: number, object: T) => unknown
): T[];
/**
* Filters elements in a object that match the properties of the given partial object.
*
* @template T
* @param {T extends Record<string, unknown> ? T : never} object - The object to iterate over.
* @param {Partial<T[keyof T]>} doesMatch - The partial object to match
* @returns {T[]} - Returns a new array of elements that match the given properties.pair.
*
* @example
* const obj = { a: { id: 1, name: 'Alice' }, b: { id: 2, name: 'Bob' } };
* filter(obj, { name: 'Bob' });
* // => [{ id: 2, name: 'Bob' }]
*/
export function filter<T extends Record<string, unknown>>(object: T, doesMatch: Partial<T[keyof T]>): T[];
/**
* Filters elements in a arr that match the given key-value pair.
*
* @template T
* @param {T extends Record<string, unknown> ? T : never} object - The object to iterate over.
* @param {[keyof T, unknown]} doesMatchProperty - The key-value pair to match.
* @returns {T[]} - Returns a new array of elements that match the given key-value pair.
*
* @example
* const obj = { alice: { id: 1, name: 'Alice' }, bob: { id: 2, name: 'Bob' } };
* filter(obj, ['name', 'Alice']);
* // => [{ id: 1, name: 'Alice' }]
*/
export function filter<T extends Record<string, unknown>>(object: T, doesMatchProperty: [keyof T, unknown]): T[];
/**
* Filters the object, returning elements that contain the given property name.
*
* @template T
* @param {T extends Record<string, unknown> ? T : never} object - The object to iterate over.
* @param {string} propertyToCheck - The property name to check.
* @returns {T[]} - Returns a new array of elements that match the given property name.
*
* @example
* const obj = { a: { id: 1, name: 'Alice' }, b: { id: 2, name: 'Bob' }, c: { id: 3, age: 28 } };
* filter(obj, 'name');
* // => [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
*/
export function filter<T extends Record<string, unknown>>(object: T, propertyToCheck: string): T[];
/**
* Iterates over the collection and filters elements based on the given predicate.
* If a function is provided, it is invoked for each element in the collection.
*
* @template T
* @param {readonly T[] | Record<any, any>} source - The array or object to iterate over.
* @param {((item: T, index: number, arr: any) => unknown) | Partial<T> | [keyof T, unknown] | string} [predicate=identity] - The function invoked per iteration.
* @returns {T[]} - Returns a new array of filtered elements that satisfy the predicate.
*
* @example
* filter([{ a: 1 }, { a: 2 }, { b: 1 }], 'a');
* // => [{ a: 1 }, { a: 2 }]
*
* filter([{ a: 1 }, { a: 2 }, { b: 1 }], { b: 1 });
* // => [{ b: 1 }]
*
* filter({ item1: { a: 0, b: true }, item2: { a: 1, b: true }, item3: { a: 2, b: false }}, { b: false })
* // => [{ a: 2, b: false }]
*
* filter([{ a: 1 }, { a: 2 }, { a: 3 }], ['a', 2]);
* // => [{ a: 2 }]
*/
export function filter<T>(
source: T[] | Record<any, any>,
predicate?: ((item: T, index: number, arr: any) => unknown) | Partial<T> | [keyof T, unknown] | string
): T[] {
if (!predicate) {
predicate = identity;
}
const collection = isArray(source) ? source : Object.values(source);
switch (typeof predicate) {
case 'function': {
return collection.filter(predicate);
}
case 'object': {
return isArray(predicate)
? collection.filter(matchesProperty(predicate[0], predicate[1]))
: collection.filter(matches(predicate));
}
case 'string': {
return collection.filter(property(predicate));
}
}
}

View File

@ -45,6 +45,7 @@ export { sortBy } from './array/sortBy.ts';
export { size } from './array/size.ts'; export { size } from './array/size.ts';
export { some } from './array/some.ts'; export { some } from './array/some.ts';
export { zipObjectDeep } from './array/zipObjectDeep.ts'; export { zipObjectDeep } from './array/zipObjectDeep.ts';
export { filter } from './array/filter.ts';
export { head as first } from '../array/head.ts'; export { head as first } from '../array/head.ts';
export { ary } from './function/ary.ts'; export { ary } from './function/ary.ts';