@vixsonous/map-array
v2.0.1
Published
A typescript class that converts arrays into a loopable, O(1) lookup HashMap
Maintainers
Readme
MapArray
A TypeScript utility class that converts arrays into an O(1) lookup HashMap while keeping them fully loopable. No dependencies, lightweight, and TypeScript-first.
Why MapArray?
When working with arrays of objects, lookups like array.find(item => item.id === id) are O(N) — they scan the entire array every time. MapArray converts your array into a HashMap on initialization (O(N) once), then every subsequent lookup is O(1).
// Without MapArray — O(N) every lookup
const user = users.find(u => u.id === "42");
// With MapArray — O(1) every lookup
const user = userMap.get("42");Immutability Note
MapArray doesn't perform a deep clone (structuredClone) of your array on initialization and will still contain references to the original array, so any mutations that affects the values in the array will also affect the original array.
This is the case if you pass the array directly into the "array" property on initialization. If you wish to have a clean array without references to the original array, perform a deep clone. Structured clone will perform a deep clone with arrays or objects multiple levels deep.
You can also pass the "clone" property on initialization.
Performance
Benchmarks run on 100,000 items, 1,000 iterations each.
| Operation | Native Array | MapArray | Per single call (Native) | Per single call (MapArray) | |------------------------|--------------|----------|--------------------------|----------------------------| | Single lookup | 440ms | 0.09ms | ~0.44ms | ~0.00009ms | | findMap (1 id) | 474ms | 0.46ms | ~0.47ms | ~0.00046ms | | findMap (100 ids) | 9,973ms | 6.23ms | ~9.97ms | ~0.006ms | | findMap (1000 ids) | 85,890ms | 60.76ms | ~85.89ms | ~0.06ms | | findForEach (100 ids) | 9,923ms | 6.253ms | ~9.97ms | ~0.06ms | | findForEach (1000 ids) | 85,792ms | 53.253ms | ~85.79ms | ~0.05ms |
Installation
npm install @vixsonous/map-arrayUsage
Array of Objects
import { MapArray } from "@vixsonous/map-array";
interface User {
id: number;
name: string;
age: number;
}
const users = [
{ id: 1, name: "Alice", age: 20 },
{ id: 2, name: "Bob", age: 24 },
{ id: 3, name: "Charlie", age: 26 },
];
const userMap = new MapArray({ array: users, idField: "id" });
userMap.get("1"); // { id: 1, name: "Alice", age: 20 }
userMap.has("2"); // true
userMap.has("99"); // falsePrimitive Array
const fruits = new MapArray({ array: ["apple", "banana", "cake"] });
fruits.has("apple"); // true
fruits.has("grape"); // falseNote: Primitive arrays are automatically deduplicated since values are used as keys.
API
Constructor
new MapArray({ array?, idField?, clone? })| Parameter | Type | Description |
|-----------|------|-------------|
| array | Array<T> | Optional. The array to convert. |
| idField | keyof T | Required if T is an object. The field to use as the HashMap key. Must be a string or number field. |
TypeScript will enforce
idFieldat compile time
get(id)
Returns the item with the given ID, or undefined if not found.
userMap.get("1"); // { id: 1, name: "Alice", age: 20 }
userMap.get("99"); // undefinedhas(id)
Returns true if an item with the given ID exists.
userMap.has("1"); // true
userMap.has("99"); // falseadd(value, id?)
Adds a new item. Optionally pass a custom ID.
userMap.add({ id: 4, name: "Diana", age: 30 });
userMap.add({ id: 5, name: "Eve", age: 28 }, "custom-key");remove(id)
Removes the item with the given ID.
userMap.remove("1");
userMap.has("1"); // falseset(id, field, value)
Updates a specific field on an existing item. Type-safe — value must match the field's type.
userMap.set("2", "name", "Bobby");
userMap.get("2"); // { id: 2, name: "Bobby", age: 24 }map(callback)
Works like Array.map() — iterates over all items and returns a new array.
const names = userMap.map(user => user.name);
// ["Alice", "Bob", "Charlie"]forEach(callback)
Works like Array.forEach() — iterates over all items.
userMap.forEach((user, index) => console.log(index, user.name));findMap(ids, callback)
Maps only over a subset of IDs instead of the entire collection — O(K) where K is the number of IDs, instead of O(N).
const result = userMap.findMap(["1", "3"], user => user.name);
// ["Alice", "Charlie"] — Bob is skipped entirelyAlso works with a single ID:
userMap.findMap("1", user => user.name);
// ["Alice"]You can also use numeric IDs
const result = userMap.findMap([1, 3], user => user.name);
// ["Alice", "Charlie"] — Bob is skipped entirelyAlso works with a single ID:
userMap.findMap(1, user => user.name);
// ["Alice"]toArray()
Converts the MapArray back to a plain array, reflecting the current state (including any additions or removals).
userMap.remove("1");
userMap.toArray(); // [{ id: 2, ... }, { id: 3, ... }]getIds()
Returns all current keys as an array of strings.
userMap.getIds(); // ["1", "2", "3"]getLength()
Returns the number of items.
userMap.getLength(); // 3Iterable
MapArray supports for...of natively:
for (const user of userMap) {
console.log(user.name);
}License
MIT
