Skip to content

opensly/fast-deep-merge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors