Skip to content

Commit

Permalink
feat(merge): support reset args
Browse files Browse the repository at this point in the history
  • Loading branch information
haoziqaq committed Nov 11, 2024
1 parent 898f570 commit fc14191
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 51 deletions.
10 changes: 5 additions & 5 deletions docs/collection/merge-with.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ mergeWith({ a: [1, 2] }, { a: [3, 4] }, (objValue, srcValue) => [...objValue, ..

### Arguments

| Arg | Type | Defaults |
| -------- | --------------------------------------------------------------------------------- | -------- |
| `object` | `object` | |
| `source` | `object` | |
| `fn` | `(objValue: any, srcValue: any, key: any, object: object, source: object) => any` | |
| Arg | Type | Defaults |
| ------------ | --------------------------------------------------------------------------------- | -------- |
| `object` | `object` | |
| `...sources` | `object[]` | |
| `fn` | `(objValue: any, srcValue: any, key: any, object: object, source: object) => any` | |

### Return

Expand Down
8 changes: 4 additions & 4 deletions docs/collection/merge.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ merge({ a: 1, b: { c: 2 } }, { b: { d: 3 }, e: 4 })

### Arguments

| Arg | Type | Defaults |
| -------- | -------- | -------- |
| `object` | `object` | |
| `source` | `object` | |
| Arg | Type | Defaults |
| ------------ | ---------- | -------- |
| `object` | `object` | |
| `...sources` | `object[]` | |

### Return

Expand Down
10 changes: 5 additions & 5 deletions docs/zh/collection/merge-with.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ mergeWith({ a: [1, 2] }, { a: [3, 4] }, (objValue, srcValue) => [...objValue, ..

### 参数

| 参数 | 类型 | 默认值 |
| -------- | --------------------------------------------------------------------------------- | ------ |
| `object` | `object` | |
| `source` | `object` | |
| `fn` | `(objValue: any, srcValue: any, key: any, object: object, source: object) => any` | |
| 参数 | 类型 | 默认值 |
| ------------ | --------------------------------------------------------------------------------- | ------ |
| `object` | `object` | |
| `...sources` | `object[]` | |
| `fn` | `(objValue: any, srcValue: any, key: any, object: object, source: object) => any` | |

### 返回值

Expand Down
8 changes: 4 additions & 4 deletions docs/zh/collection/merge.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ merge({ a: 1, b: { c: 2 } }, { b: { d: 3 }, e: 4 })

### 参数

| 参数 | 类型 | 默认值 |
| -------- | -------- | ------ |
| `object` | `object` | |
| `source` | `object` | |
| 参数 | 类型 | 默认值 |
| ------------ | ---------- | ------ |
| `object` | `object` | |
| `...sources` | `object[]` | |

### 返回值

Expand Down
4 changes: 2 additions & 2 deletions src/collection/merge.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { mergeWith } from './mergeWith'

export function merge<T extends Record<string, any>, K extends Record<string, any>>(object: T, source: K) {
return mergeWith(object, source, () => undefined)
export function merge<T extends Record<string, any>, K extends Record<string, any>>(object: T, ...sources: K[]) {
return mergeWith(object, ...sources, () => undefined)
}
65 changes: 43 additions & 22 deletions src/collection/mergeWith.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,55 @@
import { hasOwn, isArray, isObject } from '../general'
import { at } from '../array'
import { hasOwn, isArray, isFunction, isObject } from '../general'

Check warning on line 2 in src/collection/mergeWith.ts

View workflow job for this annotation

GitHub Actions / test

'isFunction' is defined but never used

type Fn = (objValue: any, srcValue: any, key: string | number | symbol, object?: any, source?: any) => any

export function mergeWith<T extends Record<string, any>, K extends Record<string, any>>(
object: T,
source: K,
fn: (objValue: any, srcValue: any, key: string | number | symbol, object?: T, source?: K) => any,
...sources: [...K[], fn: Fn]
): T & K {
function baseMerge(target: any, src: any): any {
// eslint-disable-next-line no-restricted-syntax
for (const key in src) {
if (hasOwn(src, key)) {
const srcValue = src[key]
const targetValue = target[key]

const customResult = fn(targetValue, srcValue, key, object, source)

if (customResult !== undefined) {
target[key] = customResult
} else if (isObject(srcValue)) {
if (isObject(targetValue)) {
target[key] = baseMerge(targetValue, srcValue)
const fn = at(sources, -1) as Fn
const targets = [object, ...sources.slice(0, -1)] as (T & K)[]

let len = targets.length - 1
let result = targets[len]

while (len) {
result = baseMergeWith(targets[len - 1], result, fn)
len--
}

function baseMergeWith<T extends Record<string, any>, K extends Record<string, any>>(
object: T,
source: K,
fn: (objValue: any, srcValue: any, key: string | number | symbol, object?: T, source?: K) => any,
): T & K {
function baseMerge(target: any, src: any): any {
// eslint-disable-next-line no-restricted-syntax
for (const key in src) {
if (hasOwn(src, key)) {
const srcValue = src[key]
const targetValue = target[key]

const customResult = fn(targetValue, srcValue, key, object, source)

if (customResult !== undefined) {
target[key] = customResult
} else if (isObject(srcValue)) {
if (isObject(targetValue)) {
target[key] = baseMerge(targetValue, srcValue)
} else {
target[key] = baseMerge(isArray(srcValue) ? [] : {}, srcValue)
}
} else {
target[key] = baseMerge(isArray(srcValue) ? [] : {}, srcValue)
target[key] = srcValue
}
} else {
target[key] = srcValue
}
}
return target
}
return target

return baseMerge(object as any, source as any) as T & K
}

return baseMerge(object as any, source as any) as T & K
return result
}
36 changes: 27 additions & 9 deletions tests/collection.spec.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import { describe, it, expect } from 'vitest'
import { merge, mergeWith, cloneDeep, cloneDeepWith, isNumber, hasOwn } from '../src'

it('should merge self', () => {
const obj = { a: 1 }
expect(merge(obj)).toBe(obj)
})

it('should merge two objects', () => {
const result = merge({ a: 1, b: { c: 2 } }, { b: { d: 3 }, e: 4 })
expect(result).toEqual({ a: 1, b: { c: 2, d: 3 }, e: 4 })
expect(merge({ a: 1, b: { c: 2 } }, { b: { d: 3 }, e: 4 })).toEqual({ a: 1, b: { c: 2, d: 3 }, e: 4 })
})

it('should handle nested merges', () => {
const result = merge({ a: { b: 1 } }, { a: { c: 2 } })
expect(result).toEqual({ a: { b: 1, c: 2 } })
it('should merge rest objects', () => {
expect(merge({ a: 1 }, { b: 2 }, { c: 3 }, { a: 4 })).toEqual({ a: 4, b: 2, c: 3 })
})

const result2 = merge({ a: 1 }, { b: { c: 1 } })
expect(result2).toEqual({ a: 1, b: { c: 1 } })
it('should handle nested merges', () => {
expect(merge({ a: { b: 1 } }, { a: { c: 2 } })).toEqual({ a: { b: 1, c: 2 } })
expect(merge({ a: 1 }, { b: { c: 1 } })).toEqual({ a: 1, b: { c: 1 } })
expect(merge({ a: 1 }, { b: { c: 1 } }, { a: 4, b: { d: 1 } })).toEqual({ a: 4, b: { c: 1, d: 1 } })
})

it('should create new properties if they don’t exist in the target', () => {
Expand All @@ -25,8 +31,20 @@ it('should handle array merging correctly', () => {
})

it('should use callback for array merging', () => {
const result = mergeWith({ a: [1, 2] }, { a: [3, 4] }, (objValue, srcValue) => [...objValue, ...srcValue])
expect(result).toEqual({ a: [1, 2, 3, 4] })
expect(mergeWith({ a: [1, 2] }, { a: [3, 4] }, (objValue, srcValue) => [...objValue, ...srcValue])).toEqual({
a: [1, 2, 3, 4],
})

expect(
mergeWith({ a: [1, 2] }, { a: [3, 4] }, { b: [5, 6] }, { b: [7, 8], d: [9, 10] }, (objValue, srcValue) => [
...(objValue ?? []),
...(srcValue ?? []),
]),
).toEqual({
a: [1, 2, 3, 4],
b: [5, 6, 7, 8],
d: [9, 10],
})
})

it('should use the callback to override values', () => {
Expand Down

0 comments on commit fc14191

Please sign in to comment.