mirror of
https://github.com/toss/es-toolkit.git
synced 2024-11-28 03:34:26 +03:00
feat(set): add set
function (#223)
* feat: set * test: set * chore: add doc and benchmark * chore: fix benchmark name
This commit is contained in:
parent
7ee48c0e71
commit
b9432e1cfd
19
benchmarks/set.bench.ts
Normal file
19
benchmarks/set.bench.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { describe, bench } from 'vitest';
|
||||
import { set } from 'es-toolkit';
|
||||
import { set as lodashSet } from 'lodash';
|
||||
|
||||
describe('set', () => {
|
||||
bench('es-toolkit/set-1', () => {
|
||||
set({}, 'a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z', 1);
|
||||
});
|
||||
bench('es-toolkit/set-2', () => {
|
||||
set({}, 'a[0][1][2][3][4][5][6]', 1);
|
||||
});
|
||||
|
||||
bench('lodash/set-1', () => {
|
||||
lodashSet({}, 'a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z', 1);
|
||||
});
|
||||
bench('lodash/set-2', () => {
|
||||
lodashSet({}, 'a[0][1][2][3][4][5][6]', 1);
|
||||
});
|
||||
});
|
@ -138,6 +138,7 @@ function sidebar(): DefaultTheme.Sidebar {
|
||||
{ text: 'pick', link: '/reference/object/pick' },
|
||||
{ text: 'pickBy', link: '/reference/object/pickBy' },
|
||||
{ text: 'invert', link: '/reference/object/invert' },
|
||||
{ text: 'set', link: '/reference/object/set' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -149,6 +149,7 @@ function sidebar(): DefaultTheme.Sidebar {
|
||||
{ text: 'pick', link: '/ko/reference/object/pick' },
|
||||
{ text: 'pickBy', link: '/ko/reference/object/pickBy' },
|
||||
{ text: 'invert', link: '/ko/reference/object/invert' },
|
||||
{ text: 'set', link: '/ko/reference/object/set' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
46
docs/ko/reference/object/set.md
Normal file
46
docs/ko/reference/object/set.md
Normal file
@ -0,0 +1,46 @@
|
||||
# set
|
||||
|
||||
지정된 경로에 주어진 값을 설정해요. 경로의 일부가 존재하지 않으면 생성됩니다.
|
||||
|
||||
## Signature
|
||||
|
||||
```typescript
|
||||
function set<T>(obj: Settable, path: Path, value: any): T;
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
- `obj` (Settable): 값을 설정할 객체예요.
|
||||
- `path` (Path): 값을 설정할 속성의 경로예요.
|
||||
- `value` (any): 설정할 값이에요.
|
||||
|
||||
### Returns
|
||||
|
||||
(`T`): 수정된 객체를 반환해요. T를 지정하지 않으면 unknown이에요.
|
||||
|
||||
## Examples
|
||||
|
||||
```typescript
|
||||
// 중첩된 객체에 값 설정
|
||||
const obj = { a: { b: { c: 3 } } };
|
||||
set(obj, 'a.b.c', 4);
|
||||
console.log(obj.a.b.c); // 4
|
||||
|
||||
// 배열에 값 설정
|
||||
const arr = [1, 2, 3];
|
||||
set(arr, 1, 4);
|
||||
console.log(arr[1]); // 4
|
||||
|
||||
// 존재하지 않는 경로 생성 및 값 설정
|
||||
const obj2 = {};
|
||||
set(obj2, 'a.b.c', 4);
|
||||
console.log(obj2); // { a: { b: { c: 4 } } }
|
||||
|
||||
// 인터페이스 사용
|
||||
interface O {
|
||||
a: number;
|
||||
}
|
||||
const obj3 = {};
|
||||
const result = set<O>(obj3, 'a', 1); // result 타입 = { a: number }
|
||||
console.log(result); // { a: 1 }
|
||||
```
|
46
docs/reference/object/set.md
Normal file
46
docs/reference/object/set.md
Normal file
@ -0,0 +1,46 @@
|
||||
# set
|
||||
|
||||
Sets the given value at the specified path of the object. If any part of the path does not exist, it will be created.
|
||||
|
||||
## Signature
|
||||
|
||||
```typescript
|
||||
function set<T>(obj: Settable, path: Path, value: any): T;
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
- `obj` (Settable): The object to modify.
|
||||
- `path` (Path): The path of the property to set.
|
||||
- `value` (any): The value to set.
|
||||
|
||||
### Returns
|
||||
|
||||
(`T`): Returns the modified object. If T is not specified, it defaults to unknown.
|
||||
|
||||
## Examples
|
||||
|
||||
```typescript
|
||||
// Set a value in a nested object
|
||||
const obj = { a: { b: { c: 3 } } };
|
||||
set(obj, 'a.b.c', 4);
|
||||
console.log(obj.a.b.c); // 4
|
||||
|
||||
// Set a value in an array
|
||||
const arr = [1, 2, 3];
|
||||
set(arr, 1, 4);
|
||||
console.log(arr[1]); // 4
|
||||
|
||||
// Create non-existent path and set value
|
||||
const obj2 = {};
|
||||
set(obj2, 'a.b.c', 4);
|
||||
console.log(obj2); // { a: { b: { c: 4 } } }
|
||||
|
||||
// Use with interface
|
||||
interface O {
|
||||
a: number;
|
||||
}
|
||||
const obj3 = {};
|
||||
const result = set<O>(obj3, 'a', 1); // typeof result = { a: number }
|
||||
console.log(result); // { a: 1 }
|
||||
```
|
@ -4,3 +4,4 @@ export { pick } from './pick.ts';
|
||||
export { pickBy } from './pickBy.ts';
|
||||
export { invert } from './invert.ts';
|
||||
export { clone } from './clone.ts';
|
||||
export { set } from './set.ts';
|
||||
|
88
src/object/set.spec.ts
Normal file
88
src/object/set.spec.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { set } from './set';
|
||||
|
||||
describe('set', () => {
|
||||
//--------------------------------------------------------------------------------
|
||||
// object
|
||||
//--------------------------------------------------------------------------------
|
||||
it('should set a value on an object', () => {
|
||||
interface Test {
|
||||
a: number;
|
||||
}
|
||||
const result = set<Test>({}, 'a', 1);
|
||||
result.a;
|
||||
expect(result).toEqual({ a: 1 });
|
||||
});
|
||||
|
||||
it('should set a value on an object with nested path', () => {
|
||||
const result = set<{ a: { b: number } }>({}, 'a.b', 1);
|
||||
expect(result).toEqual({ a: { b: 1 } });
|
||||
});
|
||||
|
||||
it('should set a value on an object with nested path', () => {
|
||||
const result = set<{ a: { b: { c: { d: number } } } }>({}, 'a.b.c.d', 1);
|
||||
expect(result).toEqual({ a: { b: { c: { d: 1 } } } });
|
||||
});
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// array
|
||||
//--------------------------------------------------------------------------------
|
||||
it('should set a value on an array', () => {
|
||||
const result = set<number[]>([], 0, 1);
|
||||
expect(result).toEqual([1]);
|
||||
expect(result[0]).toEqual(1);
|
||||
});
|
||||
|
||||
it('should set a value on an array with nested path', () => {
|
||||
const result = set<number[][]>([], '0.0', 1);
|
||||
expect(result).toEqual([[1]]);
|
||||
expect(result[0][0]).toEqual(1);
|
||||
});
|
||||
|
||||
it('should set a value on an array with nested path', () => {
|
||||
const result = set<number[][][]>([], '0.0.0', 1);
|
||||
expect(result).toEqual([[[1]]]);
|
||||
expect(result[0][0][0]).toEqual(1);
|
||||
});
|
||||
it('should set a value on an array with nested path', () => {
|
||||
const arr = [1, 2, 3];
|
||||
set(arr, 1, 4);
|
||||
expect(arr).toEqual([1, 4, 3]);
|
||||
expect(arr[1]).toEqual(4);
|
||||
});
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// object and array
|
||||
//--------------------------------------------------------------------------------
|
||||
it('should set a value on an object and array', () => {
|
||||
const result = set<Array<{ a: number }>>([], '0.a', 1);
|
||||
expect(result).toEqual([{ a: 1 }]);
|
||||
expect(result[0].a).toEqual(1);
|
||||
});
|
||||
|
||||
it('should set a value on an object and array', () => {
|
||||
const result = set<{ a: number[] }>({}, 'a.0', 1);
|
||||
expect(result).toEqual({ a: [1] });
|
||||
expect(result.a[0]).toEqual(1);
|
||||
});
|
||||
|
||||
it('should set a value on an object and array', () => {
|
||||
const result = set<{ a: number[][] }>({}, 'a.0.0', 1);
|
||||
expect(result).toEqual({ a: [[1]] });
|
||||
expect(result.a[0][0]).toEqual(1);
|
||||
});
|
||||
|
||||
it('should set a value on an object and array', () => {
|
||||
const result = set<{ a: number[][][] }>({}, 'a[0][0][0]', 1);
|
||||
expect(result).toEqual({ a: [[[1]]] });
|
||||
expect(result.a[0][0][0]).toEqual(1);
|
||||
});
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// not support map and set
|
||||
//--------------------------------------------------------------------------------
|
||||
it('not support map and set', () => {
|
||||
expect(() => set<Map<string, number>>(new Map(), 'a', 1)).toThrow(TypeError);
|
||||
expect(() => set<Set<number>>(new Set(), 1, 2)).toThrow(TypeError);
|
||||
});
|
||||
});
|
57
src/object/set.ts
Normal file
57
src/object/set.ts
Normal file
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Sets the value at the specified path of the given object. If any part of the path does not exist, it will be created.
|
||||
*
|
||||
* @template T - The type of the object.
|
||||
* @param {Settable} obj - The object to modify.
|
||||
* @param {Path} path - The path of the property to set.
|
||||
* @param {any} value - The value to set.
|
||||
* @returns {T} - The modified object.
|
||||
*
|
||||
* @example
|
||||
* // Set a value in a nested object
|
||||
* const obj = { a: { b: { c: 3 } } };
|
||||
* set(obj, 'a.b.c', 4);
|
||||
* console.log(obj.a.b.c); // 4
|
||||
*
|
||||
* @example
|
||||
* // Set a value in an array
|
||||
* const arr = [1, 2, 3];
|
||||
* set(arr, 1, 4);
|
||||
* console.log(arr[1]); // 4
|
||||
*
|
||||
* @example
|
||||
* // Create non-existent path and set value
|
||||
* const obj = {};
|
||||
* set(obj, 'a.b.c', 4);
|
||||
* console.log(obj); // { a: { b: { c: 4 } } }
|
||||
*/
|
||||
export function set<T>(obj: Settable, path: Path, value: any): T {
|
||||
if (obj instanceof Map || obj instanceof Set) {
|
||||
throw new TypeError('Set or Map is not supported');
|
||||
}
|
||||
//TODO: memoize
|
||||
const keys = Array.isArray(path)
|
||||
? path
|
||||
: String(path as string)
|
||||
.replace(/\[|\]/g, match => {
|
||||
return match === '[' ? '.' : '';
|
||||
})
|
||||
.split('.');
|
||||
|
||||
let pointer: any = obj;
|
||||
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
const key = keys[i];
|
||||
const nextKey = keys[i + 1];
|
||||
if (pointer[key] == null || typeof pointer[key] !== 'object') {
|
||||
pointer[key] = /^\d+$/.test(nextKey as string) ? [] : {};
|
||||
}
|
||||
pointer = pointer[key];
|
||||
}
|
||||
|
||||
pointer[keys[keys.length - 1]] = value;
|
||||
return obj as T;
|
||||
}
|
||||
|
||||
type Settable = object | any[];
|
||||
type Path = string | number | Array<string | number>;
|
Loading…
Reference in New Issue
Block a user