npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

deep-merge-many

v2.0.1

Published

Deep-merge many plain objects in one call — fast at scale, min/max on numeric leaves.

Downloads

753

Readme

deep-merge-many

CI npm version License: MIT

Deep-merge many plain objects in one call. Recurses into nested objects; combines numeric leaves with Math.max, except keys named min which use Math.min.

Optimized for merging large batches (10+ objects) in a single pass — see benchmarks.

Try it: Interactive demo — edit several JSON objects and see the merged result live.

Why this exists

deep-merge-many started as in-app merge logic for a product that filters search results by per-user, per-item eligibility on our side. We could not express that in a single Algolia query, so we ran many searches in parallel (different filter sets / curated collections), applied eligibility to the hits, and then had to present one facet panel for the combined result set.

Each Algolia search response includes:

  • facets — facet value counts ({ [facetName]: { [value]: number } })
  • facets_stats — min / max (and related stats) for numeric facets ({ [facetName]: FacetStats })

After merging hits from several responses, the UI still needs a union of that metadata across every query that contributed results:

| Field | Goal when combining responses | | --- | --- | | facets | For each facet value, keep the highest count seen in any response (a value visible in any query should count). | | facets_stats | Widen numeric ranges: max of each max, min of each min (and the same max rule for other numeric stat fields). |

deepMergeMany encodes exactly that: numeric leaves use Math.max, except keys named min which use Math.min. Nested objects (facet names, then values or stat keys) are merged recursively. The helper was extracted into this small, dependency-free package so the same semantics are reusable anywhere you merge many plain objects at scale—not only Algolia.

flowchart LR
  eligibility[Per-user item eligibility]
  filters[Many Algolia filter sets]
  queries[Parallel search queries]
  meta["facets + facets_stats per response"]
  merge[deepMergeMany]
  ui[Unified facet UI]
  eligibility --> filters --> queries --> meta --> merge --> ui

Behavior

  • Nested plain objects are merged recursively (arrays and other types are leaves).
  • Numeric leaves use Math.max by default.
  • Keys named min use Math.min instead.
  • Empty entries, undefined, and {} are skipped when collecting keys.

Install

npm install deep-merge-many

Usage

import { deepMergeMany } from "deep-merge-many";

const merged = deepMergeMany([
  { bounds: { price: { min: 10, max: 100 } }, counts: { a: 3, b: 1 } },
  { bounds: { price: { min: 5, max: 200 } }, counts: { a: 1, b: 5 } },
  // …more pages or chunks
]);
// {
//   bounds: { price: { min: 5, max: 200 } },
//   counts: { a: 3, b: 5 },
// }

Algolia facets and facets_stats

After parallel queries, merge the facet metadata from each response (omit undefined / empty objects if a query returned no facets):

import { deepMergeMany } from "deep-merge-many";
import type { BaseSearchResponse } from "algoliasearch/lite";

const responses: BaseSearchResponse[] = /* parallel search results */;

const facets = deepMergeMany(
  responses.map((r) => r.facets).filter(Boolean),
) as NonNullable<BaseSearchResponse["facets"]>;

const facets_stats = deepMergeMany(
  responses.map((r) => r.facets_stats).filter(Boolean),
) as NonNullable<BaseSearchResponse["facets_stats"]>;

Example: two responses for the same brand facet — counts take the max per value; stats widen min/max across queries:

deepMergeMany([
  { brand: { Nike: 10, Adidas: 3 }, price: { min: 20, max: 100, avg: 50 } },
  { brand: { Nike: 4, Puma: 7 }, price: { min: 5, max: 200, avg: 80 } },
]);
// {
//   brand: { Nike: 10, Adidas: 3, Puma: 7 },
//   price: { min: 5, max: 200, avg: 80 },
// }

The export is deepMergeMany — one function, any number of input objects.

Development

Requires pnpm 11+ and Node 18+.

pnpm install
pnpm test
pnpm run build

See CONTRIBUTING.md for pull requests and PUBLISHING.md for npm releases.

Benchmarks

Multi-object merge throughput (5 → 100 nested objects) vs @fastify/deepmerge, ts-deepmerge, deepmerge, and deep-merge.

deep-merge-many leads from ~10 objects upward on identical payloads. Other libraries use different merge rules — this measures throughput, not identical output.

Benchmark: merge throughput vs number of objects

Bundle weight (each library’s merge entry point, esbuild-bundled for the browser, minified, gzip level 9):

Benchmark: bundled library size

pnpm bench
open benchmark/chart.html

Regenerates benchmark/chart.html, docs/benchmark.svg, docs/benchmark.png, and docs/benchmark-size.svg / .png.

License

MIT