hashmap

August 6, 2026 · View on GitHub

A small HashMap class for JavaScript — any key type, stable API since 2012, rewritten for 3.0.

Works in Node.js (14+) and the browser. ESM, CommonJS, TypeScript types, and an IIFE build included.

NPM

npm install hashmap
import HashMap from 'hashmap'
// or
const HashMap = require('hashmap')

Why this exists

Native Map didn't exist when this library was created. It does now — and for most new code, Map is the right default.

hashmap is still worth reaching for when you need more than Map gives you:

hashmapMapWeakMap
Any key type (null, numbers, strings, objects, …)yesyesobjects only
Object keys by identityyesyesyes
Iterate all entries / .sizeyesyesno
Value-based equality for Date, RegExp, arraysyesnono
Reverse lookup (.search(value))yesnono
Method chaining on mutatorsyespartialno

Use hashmap when:

  • You need custom key equality — two Date objects with the same timestamp, two /foo/ regexes, arrays compared element-wise.
  • You need .search() — find the key for a value.
  • You're maintaining legacy code that already depends on this API (plugin systems, protobuf registries, object-key pools, etc.).
  • You want a stable, zero-dependency map that behaves the same in Node and the browser.

Use native Map when: string/number/symbol keys with === equality is enough. That's most apps.

Use WeakMap when: keys are objects and you want them garbage-collected when nothing else references them. You can't iterate a WeakMap.


Quick start

const map = new HashMap()

map.set('1', 'string one')
map.set(1, 'number one')
map.get('1') // 'string one'
map.get(1)   // 'number one'

const a = {}
const b = {}
map.set(a, 'first')
map.set(b, 'second')
map.get(a) // 'first' — object identity, not stringification

Method chaining

new HashMap()
  .set(1, 'one')
  .set(2, 'two')
  .forEach((value, key) => console.log(key, value))

Iteration

map.forEach((value, key) => { /* … */ })

for (const { key, value } of map) {
  console.log(key, value)
}

Reverse lookup

map.set('id', 42)
map.search(42) // 'id'

API

Constructor

  • new HashMap() — empty map
  • new HashMap(other) — copy from another HashMap
  • new HashMap([['k1', 'v1'], ['k2', 'v2']]) — from pairs array
  • new HashMap(k1, v1, k2, v2, …) — from arguments

Methods

MethodDescription
get(key)Value for key, or undefined
set(key, value)Store a pair (chainable)
has(key)Whether key exists
delete(key)Remove by key (chainable)
search(value)Key for value, or null
clear()Remove all entries (chainable)
forEach(fn, ctx?)Iterate (value, key) (chainable)
keys() / values() / entries()Arrays of keys, values, or [key, value] pairs
clone()Shallow copy as a new HashMap
copy(other)Copy entries from other into this map (chainable)
multi(k1, v1, …)Set several pairs at once (chainable)
sizeNumber of entries

Deprecated aliases kept for compatibility: remove()delete(), count()size, type().

TypeScript

Types ship with the package. Generics work as expected:

import HashMap from 'hashmap'

const cache = new HashMap<string, number>()
cache.set('answer', 42)

Upgrading from 2.x / @types/hashmap

Types ship with the package since 3.0.0 — remove DefinitelyTyped:

npm uninstall @types/hashmap

@types/hashmap used export = HashMap (CommonJS assignment import). 3.0 uses a default export:

import HashMap from 'hashmap'
// import HashMap = require('hashmap')  // still works in TS with esModuleInterop

hash() was removed (internal helper, no known dependents). Deprecated remove(), count(), and type() remain. See docs/backwards-compatibility.md.


Browser

For a script tag without a bundler, use the IIFE build:

<script src="node_modules/hashmap/dist/hashmap.iife.js"></script>
<script>
  const map = new HashMap()
  map.set(document.body, 'root')
</script>

Benchmarks

3.0 rewrote the internals (numeric hashing, flat entry storage, eager Map index, cached entry hashes). Measured on Node 24, ~250 ms per benchmark, string keys unless noted. Run locally:

npm run benchmark
npm run benchmark -- -o tmp/current.json --compare tmp/benchmark-baseline.json

vs 2.4.0

Operation2.4.03.0.0Change
set226K266K1.2×
get224K298K1.3×
has237K268K1.1×
delete210K264K1.3×
`set$ (\text{replace} \text{existing} \text{key})7\text{K}193\text{K}**26 \times **
getget (\text{map} \text{with} 1024 \text{entries})10\text{K}273\text{K}**27 \times **
$forEach` (1024 entries)29K1.7M60×
`keys$ (1024 \text{entries})26\text{K}722\text{K}**28 \times **
$clone` (1024 entries)11K19K1.7×
copy (1024 entries)12K19K1.6×
set after delete (1024 entries)238K91K0.4×

Summary: 18 of 20 benchmarks faster than 2.4.0. Biggest wins: replacing/updating keys, iteration, and clone/copy. Remaining gaps: burst inserts into an empty map and delete-then-set on large maps.

Values are ops/sec (higher is better). Native Map is faster on plain get with string keys — hashmap trades that for custom equality, .search(), and the legacy API.


Development

npm test        # types + unit tests
npm run build   # dist (esm, cjs, iife, dts)
npm run lint

License

MIT © Ariel Flesler