feat(omit): Add compatibility version of omit

This commit is contained in:
raon0211 2024-09-14 22:56:15 +09:00
parent 0edff25215
commit 1a5e9c8e64
7 changed files with 226 additions and 0 deletions

View File

@ -26,3 +26,15 @@ const obj = { a: 1, b: 2, c: 3 };
const result = omit(obj, ['b', 'c']);
// 結果は次のようになります { a: 1 }
```
## Lodashとの互換性
`es-toolkit/compat`から`omit`関数をインポートすると、深いパスを省略することができます。
```typescript
import { omit } from 'es-toolkit/compat';
const obj = { a: { b: { c: 1 } }, d: { e: 2 }, f: { g: 3 }, 'f.g': 4 };
const result = omit(obj, ['a.b.c', 'f.g']);
// result will be { a: { b: {} }, d: { e: 2 }, f: { g: 3 } }
```

View File

@ -26,3 +26,15 @@ const obj = { a: 1, b: 2, c: 3 };
const result = omit(obj, ['b', 'c']);
// 결과는 다음과 같아요 { a: 1 }
```
## Lodash와 호환성
`es-toolkit/compat`에서 `omit` 함수를 가져오면, 깊은 경로를 제외할 수 있어요.
```typescript
import { omit } from 'es-toolkit/compat';
const obj = { a: { b: { c: 1 } }, d: { e: 2 }, f: { g: 3 }, 'f.g': 4 };
const result = omit(obj, ['a.b.c', 'f.g']);
// result will be { a: { b: {} }, d: { e: 2 }, f: { g: 3 } }
```

View File

@ -27,3 +27,15 @@ const obj = { a: 1, b: 2, c: 3 };
const result = omit(obj, ['b', 'c']);
// result will be { a: 1 }
```
## Compatibility with Lodash
The `omit` function from `es-toolkit/compat` allows you to exclude deep properties from an object.
```typescript
import { omit } from 'es-toolkit/compat';
const obj = { a: { b: { c: 1 } }, d: { e: 2 }, f: { g: 3 }, 'f.g': 4 };
const result = omit(obj, ['a.b.c', 'f.g']);
// result will be { a: { b: {} }, d: { e: 2 }, f: { g: 3 } }
```

View File

@ -26,3 +26,15 @@ const obj = { a: 1, b: 2, c: 3 };
const result = omit(obj, ['b', 'c']);
// result 将会是 { a: 1 }
```
## 与 Lodash 的兼容性
`es-toolkit/compat` 中的 `omit` 函数可以省略深层路径的属性。
```typescript
import { omit } from 'es-toolkit/compat';
const obj = { a: { b: { c: 1 } }, d: { e: 2 }, f: { g: 3 }, 'f.g': 4 };
const result = omit(obj, ['a.b.c', 'f.g']);
// result will be { a: { b: {} }, d: { e: 2 }, f: { g: 3 } }
```

View File

@ -55,6 +55,7 @@ export { rearg } from './function/rearg.ts';
export { get } from './object/get.ts';
export { set } from './object/set.ts';
export { pick } from './object/pick.ts';
export { omit } from './object/omit.ts';
export { has } from './object/has.ts';
export { property } from './object/property.ts';
export { mapKeys } from './object/mapKeys.ts';

View File

@ -0,0 +1,84 @@
import { describe, expect, it } from 'vitest';
import { omit } from './omit';
import { toArgs } from '../_internal/toArgs';
import { objectProto } from '../_internal/objectProto';
import { stringProto } from '../_internal/stringProto';
describe('omit', () => {
it('should omit deep properties', () => {
const obj = { a: { b: { c: 1 } }, d: { e: 2 }, f: { g: 3 }, 'f.g': 4 };
const result = omit(obj, ['a.b.c', 'f.g']);
expect(result).toEqual({ a: { b: {} }, d: { e: 2 }, f: { g: 3 } });
});
const args = toArgs(['a', 'c']);
const object = { a: 1, b: 2, c: 3, d: 4 };
const nested = { a: 1, b: { c: 2, d: 3 } };
it('should flatten `paths`', () => {
expect(omit(object, 'a', 'c')).toEqual({ b: 2, d: 4 });
expect(omit(object, ['a', 'd'], 'c')).toEqual({ b: 2 });
});
it('should support deep paths', () => {
expect(omit(nested, 'b.c')).toEqual({ a: 1, b: { d: 3 } });
});
it('should support path arrays', () => {
const object = { 'a.b': 1, a: { b: 2 } };
const actual = omit(object, [['a.b']]);
expect(actual).toEqual({ a: { b: 2 } });
});
it('should omit a key over a path', () => {
const object = { 'a.b': 1, a: { b: 2 } };
['a.b', ['a.b']].forEach(path => {
expect(omit(object, path)).toEqual({ a: { b: 2 } });
});
});
it('should coerce `paths` to strings', () => {
expect(omit({ 0: 'a' }, 0)).toEqual({});
});
it('should return an empty object when `object` is nullish', () => {
[null, undefined].forEach(value => {
objectProto.a = 1;
const actual = omit(value, 'valueOf');
delete objectProto.a;
expect(actual).toEqual({});
});
});
// Manipulating prototypes is an anti-pattern
it.skip('should work with a primitive `object`', () => {
stringProto.a = 1;
stringProto.b = 2;
// eslint-disable-next-line
// @ts-ignore
expect(omit('', 'b').a).toEqual(1);
// eslint-disable-next-line
// @ts-ignore
expect(omit('', 'b').b).not.toEqual(2);
delete stringProto.a;
delete stringProto.b;
});
it('should work with `arguments` object `paths`', () => {
// eslint-disable-next-line
// @ts-ignore
expect(omit(object, args)).toEqual({ b: 2, d: 4 });
});
it('should not mutate `object`', () => {
['a', ['a'], 'a.b', ['a.b']].forEach(path => {
const object = { a: { b: 2 } };
omit(object, path);
expect(object).toEqual({ a: { b: 2 } });
});
});
});

93
src/compat/object/omit.ts Normal file
View File

@ -0,0 +1,93 @@
import { cloneDeep } from '../../object/cloneDeep.ts';
import { unset } from './unset.ts';
/**
* Creates a new object with specified keys omitted.
*
* This function takes an object and an array of keys, and returns a new object that
* excludes the properties corresponding to the specified keys.
*
* @template T - The type of object.
* @template K - The type of keys in object.
* @param {T} obj - The object to omit keys from.
* @param {K[]} keys - An array of keys to be omitted from the object.
* @returns {Omit<T, K>} A new object with the specified keys omitted.
*
* @example
* const obj = { a: 1, b: 2, c: 3 };
* const result = omit(obj, ['b', 'c']);
* // result will be { a: 1 }
*/
export function omit<T extends Record<string, any>, K extends keyof T>(obj: T, keys: readonly K[]): Omit<T, K>;
/**
* Creates a new object with specified keys omitted.
*
* This function takes an object and a variable number of keys, and returns a new object that
* excludes the properties corresponding to the specified keys.
*
* Deep keys can be specified for keys.
*
* @template T - The type of object.
* @param {T} obj - The object to omit keys from.
* @param {...(PropertyKey | PropertyKey[] | PropertyKey[][]} keys - A variable number of keys to be omitted from the object.
* @returns {Partial<T>} A new object with the specified keys omitted.
*/
export function omit<T extends {}>(
obj: T | null | undefined,
...keys: Array<PropertyKey | readonly PropertyKey[] | readonly (readonly PropertyKey[])[]>
): Partial<T>;
/**
* Creates a new object with specified keys omitted.
*
* This function takes an object and a variable number of keys, and returns a new object that
* excludes the properties corresponding to the specified keys. Note that keys can be deep.
*
* Deep keys can be specified for keys.
*
* @template T - The type of object.
* @param {T} obj - The object to omit keys from.
* @param {...(PropertyKey | PropertyKey[] | PropertyKey[][])} keysArr - A variable number of keys to be omitted from the object.
* @returns {Partial<T>} A new object with the specified keys omitted.
*/
export function omit<T extends {}>(
obj: T | null | undefined,
...keysArr: Array<PropertyKey | readonly PropertyKey[] | readonly (readonly PropertyKey[])[]>
): Partial<T> {
if (obj == null) {
return {};
}
const result = cloneDeep(obj);
for (let i = 0; i < keysArr.length; i++) {
let keys = keysArr[i];
switch (typeof keys) {
case 'object': {
if (!Array.isArray(keys)) {
// eslint-disable-next-line
// @ts-ignore
keys = Array.from(keys) as PropertyKey[];
}
for (let j = 0; j < keys.length; j++) {
const key = keys[j];
unset(result, key);
}
break;
}
case 'string':
case 'symbol':
case 'number': {
unset(result, keys);
break;
}
}
}
return result;
}