Skip to main content

mergeWith()

mergeWith<T, U>(object, source, customizer): T & U

Recursively merges objects using a customizer function to resolve conflicts.

note

If customizer returns undefined, merging is handled by the default strategy.


Type Parametersโ€‹

T: T extends Record<string, unknown>โ€‹

The type of the target object.

U: U extends Record<string, unknown>โ€‹

The type of the source object.


Parametersโ€‹

object: Tโ€‹

The destination object.

source: Uโ€‹

The source object.

customizer: (objValue, srcValue, key, object, source) => unknownโ€‹

The function to customize assigned values.


Returns: T & Uโ€‹

A new deeply merged object.


Sinceโ€‹

2.0.0


Noteโ€‹

Arrays are replaced by default unless customizer handles them.


Performanceโ€‹

O(n) where n is total number of properties.


Also known asโ€‹

mergeDeepWith (Ramda) ยท mergeWith (Lodash, es-toolkit) ยท โŒ (Remeda, Radashi, Effect, Modern Dash, Antfu)


Exampleโ€‹

// Concatenate arrays instead of replacing
mergeWith(
{ items: [1, 2] },
{ items: [3, 4] },
(objValue, srcValue) => {
if (Array.isArray(objValue) && Array.isArray(srcValue)) {
return objValue.concat(srcValue);
}
return undefined; // Use default merge
}
);
// => { items: [1, 2, 3, 4] }

// Sum numeric values
mergeWith(
{ a: 1, b: 2 },
{ a: 3, b: 4 },
(objValue, srcValue) => {
if (typeof objValue === 'number' && typeof srcValue === 'number') {
return objValue + srcValue;
}
return undefined;
}
);
// => { a: 4, b: 6 }

How it works?โ€‹

Recursively merges objects using a customizer to resolve conflicts.

Array Concatenationโ€‹

Sum Valuesโ€‹

mergeDeep vs mergeWithโ€‹

mergeDeepmergeWith
CustomizationNoneFull control
ArraysReplaceCustomizable
ConflictsLeft/Right winsCustom logic

Use Casesโ€‹

Custom Array Merging strategies ๐Ÿ“Œโ€‹

Merge objects with custom array handling. Essential for flexible data merging.

import { mergeWith } from "pithos/arkhe/object/merge-with";

const baseConfig = {
plugins: ["plugin-a", "plugin-b"],
rules: { semi: "error" },
};

const extendedConfig = {
plugins: ["plugin-c"],
rules: { quotes: "warn" },
};

// Concatenate arrays instead of replacing
const merged = mergeWith(baseConfig, extendedConfig, (objValue, srcValue) => {
if (Array.isArray(objValue) && Array.isArray(srcValue)) {
return [...objValue, ...srcValue];
}
return undefined; // Use default merge for non-arrays
});

console.log(merged);
// { plugins: ["plugin-a", "plugin-b", "plugin-c"], rules: { semi: "error", quotes: "warn" } }

Numeric Value Accumulation ๐Ÿ“Œโ€‹

Accumulate numeric values during merge. Critical for aggregating statistics.

import { mergeWith } from "pithos/arkhe/object/merge-with";

const week1Stats = {
visits: 1000,
clicks: 150,
conversions: 25,
};

const week2Stats = {
visits: 1200,
clicks: 180,
conversions: 30,
};

// Sum numeric values
const totalStats = mergeWith(week1Stats, week2Stats, (objValue, srcValue) => {
if (typeof objValue === "number" && typeof srcValue === "number") {
return objValue + srcValue;
}
return undefined;
});

console.log(totalStats);
// { visits: 2200, clicks: 330, conversions: 55 }

Conditional Overwrite logicโ€‹

Apply conditional rules when merging values. Important for complex merge scenarios.

import { mergeWith } from "pithos/arkhe/object/merge-with";

const defaultSettings = {
timeout: 30000,
retries: 3,
cache: { enabled: true, ttl: 3600 },
};

const userSettings = {
timeout: -1, // Invalid value
retries: 5,
cache: { enabled: false, ttl: 0 }, // 0 is invalid for ttl
};

// Only accept valid values
const validatedSettings = mergeWith(defaultSettings, userSettings, (objValue, srcValue, key) => {
// Reject negative timeouts
if (key === "timeout" && typeof srcValue === "number" && srcValue < 0) {
return objValue;
}
// Reject zero TTL
if (key === "ttl" && srcValue === 0) {
return objValue;
}
return undefined; // Use default merge
});

console.log(validatedSettings);
// { timeout: 30000, retries: 5, cache: { enabled: false, ttl: 3600 } }