Skip to content

Commit

Permalink
feat(findKey): add findKey to compat layer (#828)
Browse files Browse the repository at this point in the history
* feat(findKey): add findKey to compat layer

* Add docs

---------

Co-authored-by: Sojin Park <[email protected]>
  • Loading branch information
Na-hyunwoo and raon0211 authored Dec 1, 2024
1 parent 976d509 commit 917aff0
Show file tree
Hide file tree
Showing 8 changed files with 378 additions and 4 deletions.
9 changes: 9 additions & 0 deletions benchmarks/performance/findKey.bench.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { bench, describe } from 'vitest';
import { findKey as findKeyToolkit } from 'es-toolkit';
import { findKey as findKeyCompatToolkit } from 'es-toolkit/compat';
import { findKey as findKeyLodash } from 'lodash';

describe('findKey', () => {
Expand All @@ -16,6 +17,10 @@ describe('findKey', () => {
bench('lodash/findKey', () => {
findKeyLodash(users, o => o.age < 40);
});

bench('es-toolkit/compat/findKey', () => {
findKeyCompatToolkit(users, o => o.age < 40);
});
});

describe('findKey/largeObject', () => {
Expand All @@ -31,4 +36,8 @@ describe('findKey/largeObject', () => {
bench('lodash/findKey', () => {
findKeyLodash(largeUsers, o => o.age === 7000);
});

bench('es-toolkit/compat/findKey', () => {
findKeyCompatToolkit(largeUsers, o => o.age === 7000);
});
});
50 changes: 46 additions & 4 deletions docs/ja/reference/object/findKey.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

提供されたテスト関数を満たすオブジェクト内の最初の要素のキーを検索します。

## Signature
## インターフェース

```typescript
function findKey<T extends Record<any, any>>(
Expand All @@ -11,16 +11,16 @@ function findKey<T extends Record<any, any>>(
): keyof T | undefined;
```

### Parameters
### パラメータ

- `obj` (`T extends Record<any, any>`): 検索するオブジェクト。
- `predicate` (`(value: T[keyof T], key: keyof T, obj: T) => boolean`): オブジェクト内の各値に対して実行する関数。

### Returns
### 戻り値

(`keyof T | undefined`): 指定されたテスト関数を満たすオブジェクト内の最初の要素のキー。テストに合格する要素がない場合は未定義です。

## Examples
##

```typescript
const users = {
Expand All @@ -32,3 +32,45 @@ const users = {
findKey(users, o => o.age < 40); // 'pebbles'
findKey(users, o => o.age > 50); // undefined
```

## Lodash 互換性

`es-toolkit/compat` から `findKey` をインポートすると、Lodash と互換になります。
キーを検索する条件を、さまざまな方法で指定できます。

- **検査関数**: 各要素に対して検査する関数を実行します。最初に `true` を返す要素のキーを返します。
- **部分オブジェクト**: 指定されたオブジェクトと部分的に一致する要素のキーを返します。
- **プロパティ-値ペア**: 指定されたプロパティと値が一致する要素のキーを返します。
- **プロパティ名**: 指定されたプロパティに対して真と評価される要素のキーを返します。

### インターフェース

```typescript
export function findKey<T extends Record<any, any>>(
obj: T,
conditionToFind: (value: T[keyof T], key: keyof T, obj: T) => boolean
): keyof T | undefined;
export function findKey<T extends Record<any, any>>(obj: T, objectToFind: Partial<T[keyof T]>): keyof T | undefined;
export function findKey<T extends Record<any, any>>(
obj: T,
propertyToFind: [keyof T[keyof T], any]
): keyof T | undefined;
export function findKey<T extends Record<any, any>>(obj: T, propertyToFind: keyof T[keyof T]): keyof T | undefined;
```

###

```typescript
const users = { barney: { age: 36 }, fred: { age: 40 } };

findKey(users, o => o.age < 40);
// => 'barney'
findKey(users, { age: 36 });
// => 'barney'
findKey(users, ['age', 36]);
// => 'barney'

const languages = { javascript: { active: false }, typescript: { active: true } };
findKey(users, 'active');
// => 'typescript'
```
43 changes: 43 additions & 0 deletions docs/ko/reference/object/findKey.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,46 @@ const users = {
findKey(users, o => o.age < 40); // 'pebbles'
findKey(users, o => o.age > 50); // undefined
```

## Lodash와 호환성

`es-toolkit/compat`에서 `findKey`를 가져오면 lodash와 완전히 호환돼요.

키를 찾는 조건을 여러 방법으로 명시할 수 있어요.

- **검사 함수**: 각각의 요소에 대해서 검사하는 함수를 실행해요. 처음으로 `true`를 반환하는 요소의 키를 반환해요.
- **부분 객체**: 주어진 객체와 부분적으로 일치하는 요소의 키를 반환해요.
- **프로퍼티-값 쌍**: 해당 프로퍼티와 값이 일치하는 요소의 키를 반환해요.
- **프로퍼티 이름**: 해당 프로퍼티에 대해서 참으로 평가되는 요소의 키를 반환해요.

### 인터페이스

```typescript
export function findKey<T extends Record<any, any>>(
obj: T,
conditionToFind: (value: T[keyof T], key: keyof T, obj: T) => boolean
): keyof T | undefined;
export function findKey<T extends Record<any, any>>(obj: T, objectToFind: Partial<T[keyof T]>): keyof T | undefined;
export function findKey<T extends Record<any, any>>(
obj: T,
propertyToFind: [keyof T[keyof T], any]
): keyof T | undefined;
export function findKey<T extends Record<any, any>>(obj: T, propertyToFind: keyof T[keyof T]): keyof T | undefined;
```

### 예시

```typescript
const users = { barney: { age: 36 }, fred: { age: 40 } };

findKey(users, o => o.age < 40);
// => 'barney'
findKey(users, { age: 36 });
// => 'barney'
findKey(users, ['age', 36]);
// => 'barney'

const languages = { javascript: { active: false }, typescript: { active: true } };
findKey(users, 'active');
// => 'typescript'
```
43 changes: 43 additions & 0 deletions docs/reference/object/findKey.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,46 @@ const users = {
findKey(users, o => o.age < 40); // 'pebbles'
findKey(users, o => o.age > 50); // undefined
```

## Compatibility with Lodash

Import `findKey` from `es-toolkit/compat` for full compatibility with lodash.

You can specify the condition for finding keys in several ways:

- **Predicate function**: You can provide a predicate function that will be applied to each value in the object. The function should return `true` for elements that match the criteria. The search continues until the predicate returns `true` for the first time.
- **Partial object**: You can also provide a partial object, and the function will return the key of the first element in the object that matches the properties of the provided object.
- **Property-value pair**: Alternatively, you can specify a property-value pair, where the function will return the key of the first element that has the specified property matching the given value.
- **Property name**: Lastly, you can provide a property name, and the function will return the key of the first element where the specified property has a truthy value.

### Signature

```typescript
export function findKey<T extends Record<any, any>>(
obj: T,
conditionToFind: (value: T[keyof T], key: keyof T, obj: T) => boolean
): keyof T | undefined;
export function findKey<T extends Record<any, any>>(obj: T, objectToFind: Partial<T[keyof T]>): keyof T | undefined;
export function findKey<T extends Record<any, any>>(
obj: T,
propertyToFind: [keyof T[keyof T], any]
): keyof T | undefined;
export function findKey<T extends Record<any, any>>(obj: T, propertyToFind: keyof T[keyof T]): keyof T | undefined;
```

### Examples

```typescript
const users = { barney: { age: 36 }, fred: { age: 40 } };

findKey(users, o => o.age < 40);
// => 'barney'
findKey(users, { age: 36 });
// => 'barney'
findKey(users, ['age', 36]);
// => 'barney'

const languages = { javascript: { active: false }, typescript: { active: true } };
findKey(users, 'active');
// => 'typescript'
```
43 changes: 43 additions & 0 deletions docs/zh_hans/reference/object/findKey.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,46 @@ const users = {
findKey(users, o => o.age < 40); // 'pebbles'
findKey(users, o => o.age > 50); // undefined
```

## Lodash 兼容性

`es-toolkit/compat` 导入 `findKey` 以实现与 lodash 的完全兼容。

您可以通过多种方式指定查找键的条件。

- **谓词函数**:对每个元素执行谓词函数。返回第一个返回 `true` 的元素的键。
- **部分对象**:返回与给定对象部分匹配的元素的键。
- **属性-值对**:返回具有指定属性和值匹配的元素的键。
- **属性名**:返回指定属性评估为真的元素的键。

### 签名

```typescript
export function findKey<T extends Record<any, any>>(
obj: T,
conditionToFind: (value: T[keyof T], key: keyof T, obj: T) => boolean
): keyof T | undefined;
export function findKey<T extends Record<any, any>>(obj: T, objectToFind: Partial<T[keyof T]>): keyof T | undefined;
export function findKey<T extends Record<any, any>>(
obj: T,
propertyToFind: [keyof T[keyof T], any]
): keyof T | undefined;
export function findKey<T extends Record<any, any>>(obj: T, propertyToFind: keyof T[keyof T]): keyof T | undefined;
```

### 示例

```typescript
const users = { barney: { age: 36 }, fred: { age: 40 } };

findKey(users, o => o.age < 40);
// => 'barney'
findKey(users, { age: 36 });
// => 'barney'
findKey(users, ['age', 36]);
// => 'barney'

const languages = { javascript: { active: false }, typescript: { active: true } };
findKey(users, 'active');
// => 'typescript'
```
1 change: 1 addition & 0 deletions src/compat/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export { assignIn as extend } from './object/assignIn.ts';
export { cloneDeep } from './object/cloneDeep.ts';
export { cloneDeepWith } from './object/cloneDeepWith.ts';
export { defaults } from './object/defaults.ts';
export { findKey } from './object/findKey.ts';
export { fromPairs } from './object/fromPairs.ts';
export { get } from './object/get.ts';
export { has } from './object/has.ts';
Expand Down
61 changes: 61 additions & 0 deletions src/compat/object/findKey.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { describe, expect, it } from 'vitest';
import { findKey } from './findKey';

/**
* @see https://lodash.com/docs/4.17.15#findKey
*/
describe('findKey', () => {
const users = {
barney: { age: 36, active: true },
fred: { age: 40, active: false },
pebbles: { age: 1, active: true },
};

it('should find key with a function predicate', function () {
const actual = findKey(users, function (o) {
return o.age < 40;
});
expect(actual).toBe('barney');
});

it('should work with `_.matches` shorthands', function () {
const actual = findKey(users, { age: 1, active: true });
expect(actual).toBe('pebbles');
});

it('should work with `_.matchesProperty` shorthands', function () {
const actual = findKey(users, ['active', false]);
expect(actual).toBe('fred');
});

it('should work with `_.property` shorthands', function () {
const actual = findKey(users, 'active');
expect(actual).toBe('barney');
});

it('should return undefined for an empty object', function () {
// @ts-expect-error - invalid argument
const actual = findKey({}, { age: 36 });
expect(actual).toBeUndefined();
});

it('should return undefined for null input', function () {
const actual = findKey(null, { age: 36 });
expect(actual).toBeUndefined();
});

it('should return undefined for undefined input', function () {
const actual = findKey(undefined, { age: 36 });
expect(actual).toBeUndefined();
});

it('should return undefined if no matching key is found', function () {
const actual = findKey(users, { age: 100 });
expect(actual).toBeUndefined();
});

it('should handle partial matches with `Partial<T[keyof T]>`', function () {
const actual = findKey(users, { active: true });
expect(actual).toBe('barney');
});
});
Loading

0 comments on commit 917aff0

Please sign in to comment.