Skip to content

opensly/fast-deep-merge

Repository files navigation

fast-deep-merge

Fast, opinionated deep merge utilities for modern node or browser.

Install

npm install fast-deep-merge

Quick start

import deepmerge, { mergeAll, deepmergeMutate, mergeStrategies } from 'fast-deep-merge';

const a = { user: { name: 'Ada' }, tags: ['core'] };
const b = { user: { role: 'admin' }, tags: ['fast'] };

const merged = deepmerge(a, b);
// => { user: { name: 'Ada', role: 'admin' }, tags: ['core', 'fast'] }

// Merge many at once
const all = mergeAll(a, b, { flags: { beta: true } });

// Mutating version for maximum speed
deepmergeMutate(a, b);

// Array strategies
mergeStrategies.replaceArrays(a, b);
mergeStrategies.uniqueArrays(a, { tags: ['core', 'ui'] });
mergeStrategies.deepArrays(
  { items: [{ id: 1, meta: { a: 1 } }] },
  { items: [{ id: 1, meta: { b: 2 } }] }
); // call the strategy directly (do not wrap with createMerger)

API

  • deepmerge(target, ...sources) — clones objects/arrays and returns a new result.
  • deepmergeWithOptions(target, ...sources, options) — same as deepmerge with custom behavior.
  • deepmergeMutate(target, ...sources) — mutates target for the fastest path.
  • mergeAll(...objects) — convenience wrapper over deepmerge.
  • createMerger(options) — returns a preconfigured merge function.
  • mergeStrategies — presets: replaceArrays, uniqueArrays, deepArrays.

Options

type MergeOptions = {
  clone?: boolean; // default true
  arrayMerge?: (target: any[], source: any[], options?: MergeOptions) => any[];
  isMergeableObject?: (value: any) => boolean; // default: plain object only
};
  • clone: false lets the merge reuse references (faster, but be aware of mutation).
  • arrayMerge customizes array handling (e.g., replace, concat, unique, deep element merge).
  • isMergeableObject controls what gets merged vs. overwritten.

Notes on performance

  • Prefer deepmergeMutate when you own the target object and can mutate it.
  • Avoid passing non-plain objects unless you provide a custom isMergeableObject.
  • For very large arrays, prefer replaceArrays to skip concatenation.

TypeScript

Types are published via index.d.ts and map 1:1 to the runtime API.

Benchmarks

Run the benchmark suite to compare performance with deepmerge:

npm install
npm run bench

Benchmarking results:

🚀 fast-deep-merge vs deepmerge

============================================================

📊 Test 1: Shallow merge (15 keys - typical config/API response)
  fast-deep-merge          7.30ms   1,370,630 ops/sec
  deepmerge               14.00ms     714,094 ops/sec
  Speedup: 1.92x faster

📊 Test 2: Deep nested merge (depth: 3, width: 4 - typical nested config)
  fast-deep-merge         37.09ms     134,797 ops/sec
  deepmerge              506.59ms       9,870 ops/sec
  Speedup: 13.66x faster

📊 Test 3: Medium arrays (50 elements each - typical list/collection)
  fast-deep-merge          5.09ms     982,519 ops/sec
  deepmerge              156.79ms      31,891 ops/sec
  Speedup: 30.80x faster

📊 Test 4: Multiple sources (3 objects - typical config override chain)
  fast-deep-merge         13.51ms     222,001 ops/sec
  deepmerge               89.47ms      33,530 ops/sec
  Speedup: 6.62x faster

Performance Summary: fast-deep-merge is ~2x to 31x faster than deepmerge across all tested scenarios, with the largest gains on array-heavy and deeply nested structures.

Results vary by Node.js version and hardware. For best performance, use Node.js 18+.

How it merges (scenario guide)

  • Plain objects: merged recursively, cloning by default (matches deepmerge).
  • Arrays (default): concatenated (matches deepmerge default).
  • Multiple sources: mergeAll(...objs) behaves like deepmerge.all.
  • Non-plain objects (Date, RegExp, Map, Set): treated as values; source overwrites target and is cloned to avoid mutation (safer; deepmerge keeps the source reference).
  • Functions: treated as values; source overwrites target (reference preserved).
  • Null / undefined: overwritten by source (matches deepmerge).
  • Primitive → object: source wins (matches deepmerge).
  • Array presets:
    • mergeStrategies.replaceArrays: overwrite arrays.
    • mergeStrategies.uniqueArrays: concat + dedupe primitives (same as deepmerge with a custom arrayMerge).
    • mergeStrategies.deepArrays: deep-merge array elements by index (differs from deepmerge, which concatenates).
  • Mutation path: deepmergeMutate merges in-place for maximum speed; deepmerge is non-mutating and clones internally.
  • Custom behavior: use deepmergeWithOptions or createMerger to provide arrayMerge / isMergeableObject (e.g., treat Maps/Sets as mergeable, or to align with deepmerge semantics).

Handling special objects

  • Plain objects/arrays are merged by default (like deepmerge).
  • Non-plain objects (Date, RegExp, Map, Set, etc.) are treated as values: the source overwrites the target, but they are cloned to avoid mutations.
  • If you need custom mergeable logic (e.g., merge Maps/Sets differently), pass isMergeableObject and arrayMerge into deepmergeWithOptions or createMerger.

License

MIT

About

Fast, opinionated deep merge utilities for modern node or browser

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published