Skip to content

Commit

Permalink
feat(propertyOf): Add propertyOf to compat (#884)
Browse files Browse the repository at this point in the history
  • Loading branch information
mass2527 authored Dec 8, 2024
1 parent 77c3509 commit b535a09
Show file tree
Hide file tree
Showing 8 changed files with 328 additions and 0 deletions.
18 changes: 18 additions & 0 deletions benchmarks/performance/propertyOf.bench.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { bench, describe } from 'vitest';
import { propertyOf as propertyOfToolkit_ } from 'es-toolkit/compat';
import { propertyOf as propertyOfLodash_ } from 'lodash';

const propertyOfToolkit = propertyOfToolkit_;
const propertyOfLodash = propertyOfLodash_;

describe('propertyOf', () => {
bench('es-toolkit/propertyOf', () => {
const getValue = propertyOfToolkit({ 'a.b': 1, a: { b: 1 } });
getValue('a.b');
});

bench('lodash/propertyOf', () => {
const getValue = propertyOfLodash({ 'a.b': 1, a: { b: 1 } });
getValue('a.b');
});
});
39 changes: 39 additions & 0 deletions docs/ja/reference/compat/object/propertyOf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# propertyOf

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

`es-toolkit/compat` からこの関数をインポートすると、[lodash と完全に同じように動作](../../../compatibility.md)します。
:::

オブジェクトの指定されたパスで値を返す関数を作成します。

`property` は特定のパスにバインドされた関数を作成し、異なるオブジェクトをクエリすることができますが、
`propertyOf` は特定のオブジェクトにバインドされた関数を作成し、そのオブジェクト内の異なるパスをクエリすることができますます。

## インターフェース

```typescript
function propertyOf(object: unknown): (path: PropertyKey | PropertyKey[]) => unknown;
```

### パラメータ

- `object` (`unknown`): クエリするオブジェクトます。

### 戻り値

(`(path: PropertyKey | PropertyKey[]) => unknown`): パスを受け取り、指定されたパスのオブジェクトから値を取得する新しい関数を返します。
ます。

##

```typescript
const getValue = propertyOf({ a: { b: { c: 3 } } });
const result = getValue('a.b.c');
console.log(result); // => 3

const getValue = propertyOf({ a: { b: { c: 3 } } });
const result = getValue(['a', 'b', 'c']);
console.log(result); // => 3
```
38 changes: 38 additions & 0 deletions docs/ko/reference/compat/object/propertyOf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# propertyOf

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

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

객체의 주어진 경로에 있는 값을 반환하는 함수를 생성해요.

[`property`](./property.md)는 특정 경로에 바인딩된 함수를 생성하여 객체로부터 값을 검색할 수 있게 하고,
`propertyOf`는 특정 객체에 바인딩된 함수를 생성하여 경로로부터 값을 검색할 수 있게 해요.

## 인터페이스

```typescript
function propertyOf(object: unknown): (path: PropertyKey | PropertyKey[]) => unknown;
```

### 파라미터

- `object` (`unknown`): 검색할 객체.

### 반환 값

(`(path: PropertyKey | PropertyKey[]) => unknown`): 경로를 입력받고 지정된 경로에서 객체의 값을 검색하는 함수.

## 예시

```typescript
const getValue = propertyOf({ a: { b: { c: 3 } } });
const result = getValue('a.b.c');
console.log(result); // => 3

const getValue = propertyOf({ a: { b: { c: 3 } } });
const result = getValue(['a', 'b', 'c']);
console.log(result); // => 3
```
38 changes: 38 additions & 0 deletions docs/reference/compat/object/propertyOf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# propertyOf

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

Creates a function that returns the value at a given path of an object.

Unlike [`property`](./property.md), which creates a function bound to a specific path and allows you to query different objects,
`propertyOf` creates a function bound to a specific object and allows you to query different paths within that object.

## Signature

```typescript
function propertyOf(object: unknown): (path: PropertyKey | PropertyKey[]) => unknown;
```

### Parameters

- `object` (`unknown`): The object to query.

### Returns

(`(path: PropertyKey | PropertyKey[]) => unknown`): Returns a new function that takes a path and retrieves the value from the object at the specified path.

## Examples

```typescript
const getValue = propertyOf({ a: { b: { c: 3 } } });
const result = getValue('a.b.c');
console.log(result); // => 3

const getValue = propertyOf({ a: { b: { c: 3 } } });
const result = getValue(['a', 'b', 'c']);
console.log(result); // => 3
```
38 changes: 38 additions & 0 deletions docs/zh_hans/reference/compat/object/propertyOf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# propertyOf

::: info
出于兼容性原因,此函数仅在 `es-toolkit/compat` 中提供。它可能具有替代的原生 JavaScript API,或者尚未完全优化。

`es-toolkit/compat` 导入时,它的行为与 lodash 完全一致,并提供相同的功能,详情请见 [这里](../../../compatibility.md)
:::

创建一个函数,该函数返回对象中特定路径的值。

`property` 不同,`property` 创建一个绑定到特定路径的函数,允许您查询不同的对象,
`propertyOf` 创建一个绑定到特定对象的函数,允许您查询该对象内的不同路径。

## 签名

```typescript
function propertyOf(object: unknown): (path: PropertyKey | PropertyKey[]) => unknown;
```

### 参数

- `object` (`unknown`): 要查询的对象。

### 返回值

(`(path: PropertyKey | PropertyKey[]) => unknown`): 返回一个新函数,该函数接受一个路径并从指定路径的对象中检索值。

## 示例

```typescript
const getValue = propertyOf({ a: { b: { c: 3 } } });
const result = getValue('a.b.c');
console.log(result); // => 3

const getValue = propertyOf({ a: { b: { c: 3 } } });
const result = getValue(['a', 'b', 'c']);
console.log(result); // => 3
```
1 change: 1 addition & 0 deletions src/compat/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export { mergeWith } from './object/mergeWith.ts';
export { omit } from './object/omit.ts';
export { pick } from './object/pick.ts';
export { property } from './object/property.ts';
export { propertyOf } from './object/propertyOf.ts';
export { set } from './object/set.ts';
export { toDefaulted } from './object/toDefaulted.ts';
export { unset } from './object/unset.ts';
Expand Down
130 changes: 130 additions & 0 deletions src/compat/object/propertyOf.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { describe, expect, it } from 'vitest';
import { propertyOf } from './propertyOf';
import { noop } from '../../function';
import { constant } from '../util/constant';
import { times } from '../util/times';

describe('propertyOf', () => {
it('should create a function that plucks a property value of a given key', () => {
const object = { a: 1 };
const propOf = propertyOf(object);

expect(propOf.length).toBe(1);
['a', ['a']].forEach(path => {
expect(propOf(path)).toBe(1);
});
});

it('should pluck deep property values', () => {
const object = { a: { b: 2 } };
const propOf = propertyOf(object);

['a.b', ['a', 'b']].forEach(path => {
expect(propOf(path)).toBe(2);
});
});

it('should pluck inherited property values', () => {
function Foo() {
// @ts-expect-error type not defined
this.a = 1;
}
Foo.prototype.b = 2;

// @ts-expect-error type not defined
const propOf = propertyOf(new Foo());

['b', ['b']].forEach(path => {
expect(propOf(path)).toBe(2);
});
});

it('should work with a non-string `path`', () => {
const array = [1, 2, 3];
const propOf = propertyOf(array);

[1, [1]].forEach(path => {
expect(propOf(path)).toBe(2);
});
});

it('should preserve the sign of `0`', () => {
const object = { '-0': 'a', 0: 'b' };
const props = [-0, Object(-0), 0, Object(0)];

const actual = props.map(key => {
const propOf = propertyOf(object);
return propOf(key);
});

expect(actual).toEqual(['a', 'a', 'b', 'b']);
});

it('should coerce `path` to a string', () => {
function fn() {}
fn.toString = constant('fn');

const expected = [1, 2, 3, 4];
const object = { null: 1, undefined: 2, fn: 3, '[object Object]': 4 };
const paths = [null, undefined, fn, {}];

times(2, index => {
const actual = paths.map(path => {
const propOf = propertyOf(object);
// @ts-expect-error invalid types
return propOf(index ? [path] : path);
});

expect(actual).toEqual(expected);
});
});

it('should pluck a key over a path', () => {
const object = { 'a.b': 1, a: { b: 2 } };
const propOf = propertyOf(object);

['a.b', ['a.b']].forEach(path => {
expect(propOf(path)).toBe(1);
});
});

it('should return `undefined` when `object` is nullish', () => {
// eslint-disable-next-line no-sparse-arrays
const values = [, null, undefined];
const expected = values.map(noop);

['constructor', ['constructor']].forEach(path => {
const actual = values.map((value, index) => {
// @ts-expect-error invalid types
const propOf = index ? propertyOf(value) : propertyOf();
return propOf(path);
});

expect(actual).toEqual(expected);
});
});

it('should return `undefined` for deep paths when `object` is nullish', () => {
// eslint-disable-next-line no-sparse-arrays
const values = [, null, undefined];
const expected = values.map(noop);

['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']].forEach(path => {
const actual = values.map((value, index) => {
// @ts-expect-error invalid types
const propOf = index ? propertyOf(value) : propertyOf();
return propOf(path);
});

expect(actual).toEqual(expected);
});
});

it('should return `undefined` if parts of `path` are missing', () => {
const propOf = propertyOf({});

['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']].forEach(path => {
expect(propOf(path)).toBe(undefined);
});
});
});
26 changes: 26 additions & 0 deletions src/compat/object/propertyOf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { get } from './get.ts';

/**
* Creates a function that returns the value at a given path of an object.
*
* Unlike `property`, which creates a function bound to a specific path and allows you to query different objects,
* `propertyOf` creates a function bound to a specific object and allows you to query different paths within that object.
*
* @param {unknown} object - The object to query.
* @returns {(path: PropertyKey | PropertyKey[]) => unknown} - Returns a new function that takes a path and retrieves the value from the object at the specified path.
*
* @example
* const getValue = propertyOf({ a: { b: { c: 3 } } });
* const result = getValue('a.b.c');
* console.log(result); // => 3
*
* @example
* const getValue = propertyOf({ a: { b: { c: 3 } } });
* const result = getValue(['a', 'b', 'c']);
* console.log(result); // => 3
*/
export function propertyOf(object: unknown): (path: PropertyKey | readonly PropertyKey[]) => unknown {
return function (path: PropertyKey | readonly PropertyKey[]) {
return get(object, path);
};
}

0 comments on commit b535a09

Please sign in to comment.