Skip to content

Commit

Permalink
feat: add pick, omit, pickBy, and omitBy functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Aybrea committed Nov 3, 2024
1 parent af238bd commit e092b86
Show file tree
Hide file tree
Showing 12 changed files with 314 additions and 1 deletion.
4 changes: 4 additions & 0 deletions docs/.vitepress/items/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@ export const collectionItems = [
{ text: 'cloneDeepWith', link: '/collection/clone-deep-with' },
{ text: 'merge', link: '/collection/merge' },
{ text: 'mergeWith', link: '/collection/merge-with' },
{ text: 'pick', link: '/collection/pick' },
{ text: 'omit', link: '/collection/omit' },
{ text: 'pickBy', link: '/collection/pick-by' },
{ text: 'omitBy', link: '/collection/omit-by' },
]
25 changes: 25 additions & 0 deletions docs/collection/omit-by.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# omitBy

Creates a new object by omitting key-value pairs based on a predicate function.

### Usage

```ts
import { omitBy } from 'rattail'

const picked = omitBy({ a: 1, b: 2, c: 3 }, (value) => value > 1)
console.log(picked) // { a: 1 }
```

### Arguments

| Arg | Type | Defaults |
| ----------- | -------------------------------------- | -------- |
| `object` | `object` | |
| `predicate` | `(value: any, key: string) => boolean` | |

### Return

| Type |
| -------- |
| `object` |
25 changes: 25 additions & 0 deletions docs/collection/omit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# omit

Creates a new object by omitting specified keys from an existing object.

### Usage

```ts
import { omit } from 'rattail'

const omitted = omit({ a: 1, b: 2, c: 3 }, ['a', 'c'])
console.log(omitted) // { b: 2 }
```

### Arguments

| Arg | Type | Defaults |
| -------- | ---------- | -------- |
| `object` | `object` | |
| `keys` | `string[]` | |

### Return

| Type |
| -------- |
| `object` |
25 changes: 25 additions & 0 deletions docs/collection/pick-by.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# pickBy

Creates a new object by picking key-value pairs based on a predicate function.

### Usage

```ts
import { pickBy } from 'rattail'

const picked = pickBy({ a: 1, b: 2, c: 3 }, (value) => value > 1)
console.log(picked) // { b: 2, c: 3 }
```

### Arguments

| Arg | Type | Defaults |
| ----------- | -------------------------------------- | -------- |
| `object` | `object` | |
| `predicate` | `(value: any, key: string) => boolean` | |

### Return

| Type |
| -------- |
| `object` |
25 changes: 25 additions & 0 deletions docs/collection/pick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# pick

Creates a new object by selecting specified keys from an existing object.

### Usage

```ts
import { pick } from 'rattail'

const picked = pick({ a: 1, b: 2, c: 3 }, ['a', 'c'])
console.log(picked) // { a: 1, c: 3 }
```

### Arguments

| Arg | Type | Defaults |
| -------- | ---------- | -------- |
| `object` | `object` | |
| `keys` | `string[]` | |

### Return

| Type |
| -------- |
| `object` |
25 changes: 25 additions & 0 deletions docs/zh/collection/omit-by.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# omitBy

通过提供的谓词函数排除键值对来创建新对象。

### 使用

```ts
import { omitBy } from 'rattail'

const picked = omitBy({ a: 1, b: 2, c: 3 }, (value) => value > 1)
console.log(picked) // { a: 1 }
```

### 参数

| 参数 | 类型 | 默认值 |
| ----------- | -------------------------------------- | ------ |
| `object` | `object` | |
| `predicate` | `(value: any, key: string) => boolean` | |

### 返回值

| 类型 |
| -------- |
| `object` |
25 changes: 25 additions & 0 deletions docs/zh/collection/omit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# omit

通过从现有对象中排除指定键来创建新对象。

### 使用

```ts
import { omit } from 'rattail'

const omitted = omit({ a: 1, b: 2, c: 3 }, ['a', 'c'])
console.log(omitted) // { b: 2 }
```

### 参数

| 参数 | 类型 | 默认值 |
| -------- | ---------- | ------ |
| `object` | `object` | |
| `keys` | `string[]` | |

### 返回值

| 类型 |
| -------- |
| `object` |
25 changes: 25 additions & 0 deletions docs/zh/collection/pick-by.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# pickBy

通过提供的谓词函数选择键值对来创建新对象。

### 使用

```ts
import { pickBy } from 'rattail'

const picked = pickBy({ a: 1, b: 2, c: 3 }, (value) => value > 1)
console.log(picked) // { b: 2, c: 3 }
```

### 参数

| 参数 | 类型 | 默认值 |
| ----------- | -------------------------------------- | ------ |
| `object` | `object` | |
| `predicate` | `(value: any, key: string) => boolean` | |

### 返回值

| 类型 |
| -------- |
| `object` |
25 changes: 25 additions & 0 deletions docs/zh/collection/pick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# pick

通过从现有对象中选择指定键来创建新对象。

### 使用

```ts
import { pick } from 'rattail'

const picked = pick({ a: 1, b: 2, c: 3 }, ['a', 'c'])
console.log(picked) // { a: 1, c: 3 }
```

### 参数

| 参数 | 类型 | 默认值 |
| -------- | ---------- | ------ |
| `object` | `object` | |
| `keys` | `string[]` | |

### 返回值

| 类型 |
| -------- |
| `object` |
49 changes: 49 additions & 0 deletions src/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,52 @@ export function cloneDeepWith<T>(value: T, fn: (value: any) => any): T {

return baseCloneDeep(value, cache)
}

export function pick<T, K extends keyof T>(object: T, keys: K[]) {
return keys.reduce(
(result, key) => {
result[key] = object[key]
return result
},
{} as Pick<T, K>,
)
}

export function omit<T extends object, K extends keyof T>(obj: T, keys: K[]): { [K2 in Exclude<keyof T, K>]: T[K2] } {
const keysToOmit = new Set(keys)
const result = {} as { [K in keyof T]: T[K] }

;(Object.keys(obj) as (keyof T)[]).forEach((key) => {
if (!keysToOmit.has(key as K)) {
result[key] = obj[key]
}
})

return result
}

export function pickBy<T extends object>(
object: T,
predicate: (value: T[keyof T], key: keyof T) => boolean,
): Partial<T> {
return Object.keys(object).reduce((result, key) => {
const typedKey = key as keyof T
if (predicate(object[typedKey], typedKey)) {
result[typedKey] = object[typedKey]
}
return result
}, {} as Partial<T>)
}

export function omitBy<T extends object>(
object: T,
predicate: (value: T[keyof T], key: keyof T) => boolean,
): Partial<T> {
return Object.keys(object).reduce((result, key) => {
const typedKey = key as keyof T
if (!predicate(object[typedKey], typedKey)) {
result[typedKey] = object[typedKey]
}
return result
}, {} as Partial<T>)
}
4 changes: 4 additions & 0 deletions src/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { omit, omitBy, pickBy } from './collection'

Check warning on line 1 in src/test.ts

View workflow job for this annotation

GitHub Actions / test

'omit' is defined but never used

Check warning on line 1 in src/test.ts

View workflow job for this annotation

GitHub Actions / test

'pickBy' is defined but never used

const picked = omitBy({ a: 1, b: 2, c: 3 }, (value) => value > 1)
console.log(picked) // { a: 1 }
58 changes: 57 additions & 1 deletion tests/collection.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest'
import { merge, mergeWith, cloneDeep, cloneDeepWith, isNumber, hasOwn } from '../src'
import { merge, mergeWith, cloneDeep, cloneDeepWith, isNumber, hasOwn, pick, omit, pickBy, omitBy } from '../src'

it('should merge two objects', () => {
const result = merge({ a: 1, b: { c: 2 } }, { b: { d: 3 }, e: 4 })
Expand Down Expand Up @@ -330,3 +330,59 @@ describe('cloneDeepWith', () => {
expect(result).toEqual({ a: 2, b: 3 })
})
})

const sampleObject = {
a: 1,
b: 'hello',
c: true,
d: null,
e: 5,
}

describe('pick', () => {
it('should pick specified keys from an object', () => {
const result = pick(sampleObject, ['a', 'b'])
expect(result).toEqual({ a: 1, b: 'hello' })
})

it('should return an empty object if no keys match', () => {
const result = pick(sampleObject, [])
expect(result).toEqual({})
})
})

describe('omit', () => {
it('should omit specified keys from an object', () => {
const result = omit(sampleObject, ['a', 'b'])
expect(result).toEqual({ c: true, d: null, e: 5 })
})

it('should return the full object if no keys are omitted', () => {
const result = omit(sampleObject, [])
expect(result).toEqual(sampleObject)
})
})

describe('pickBy', () => {
it('should pick properties based on a predicate function', () => {
const result = pickBy(sampleObject, (value) => typeof value === 'number')
expect(result).toEqual({ a: 1, e: 5 })
})

it('should return an empty object if no properties match the predicate', () => {
const result = pickBy(sampleObject, (value) => typeof value === 'object')
expect(result).toEqual({})

Check failure on line 374 in tests/collection.spec.ts

View workflow job for this annotation

GitHub Actions / test

tests/collection.spec.ts > pickBy > should return an empty object if no properties match the predicate

AssertionError: expected { d: null } to deeply equal {} - Expected + Received - Object {} + Object { + "d": null, + } ❯ tests/collection.spec.ts:374:20
})
})

describe('omitBy', () => {
it('should omit properties based on a predicate function', () => {
const result = omitBy(sampleObject, (value) => value === null || value === true)
expect(result).toEqual({ a: 1, b: 'hello', e: 5 })
})

it('should return the full object if no properties match the predicate', () => {
const result = omitBy(sampleObject, (value) => typeof value === 'symbol')
expect(result).toEqual(sampleObject)
})
})

0 comments on commit e092b86

Please sign in to comment.