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

imh

v0.0.6

Published

The extremely fast immutable helper

Readme

imh (Immutable Helper)

The extremely fast immutable helper

Installation

$ npm install --save imh

Benchmarks

Single mutation

| Library | Read | Write | Total | | ------------------------------- | ---: | -----: | -----: | | immutable.js (fastest) | 265 | 372 | 637 | | imh | 59 | 635 | 694 | | timm | 43 | 662 | 705 | | immutable-assign | 58 | 806 | 864 | | immhelper | 55 | 1,049 | 1,104 | | seamless-immutable (production) | 56 | 13,630 | 13,686 | | immer | 46 | 18,386 | 18,432 | | update-immutable | 44 | 38,532 | 38,576 | | immutability-helper | 50 | 38,666 | 38,716 |

Multiple mutations

| Library | Read | Write | Total | | -------------------------- | ---: | ----: | ----: | | update-immutable (fastest) | 1 | 97 | 98 | | imh | 1 | 198 | 199 | | immutability-helper | 3 | 263 | 266 | | immhelper | 0 | 303 | 303 | | immutable.js | 170 | 538 | 708 | | immer | 1 | 1,151 | 1,152 | | timm | 2 | 1,710 | 1,712 |

Hence, what I recommend (from top to bottom):

  1. If you don't need immutability, well... just mutate in peace! I mean, in place
  2. If you need a complete, well-tested, rock-solid library and don't mind using a non-native API for reads: use ImmutableJS
  3. If you value using plain arrays/objects above other considerations, use imh
  4. If your typical use cases involve much more reading than writing, use imh as well
  5. If you do a lot of writes, updating items in long arrays or attributes in fat objects, use ImmutableJS

Usage

import imh from "imh";

let state = {
  todos: [{ id: 1, title: "Todo 1", completed: false }],
  stats: {
    all: 1,
    active: 1,
    completed: 0,
  },
};

const StatsMutation = (current) =>
  // mutate stats prop
  imh.prop("stats", {
    all: current.todos.length,
    // compute number of active todos
    active: current.todos.filter((todo) => !todo.completed).length,
    // compute number of completed todos
    completed: current.todos.filter((todo) => todo.completed).length,
  });

function AddTodo(id, title) {
  state = imh(state, [
    // push new item to todos array
    imh.prop("todos", imh.push({ id, title, completed: true })),
    // update stats
    StatsMutation,
  ]);
}

function ToggleTodo(id) {
  state = imh(state, [
    // perform toggle action
    imh.prop(
      // nested prop path
      [
        // todos prop
        "todos",
        // toggle item which has id equal to given id
        (todo) => todo.id === id,
        // completed prop
        "completed",
      ],
      // toggle boolean value: true => false, false => true
      imh.toggle()
      // we can pass arrow function to mutate value as well
      // completed => !completed
    ),
    // update stats
    StatsMutation,
  ]);
}

AddTodo(2, "Todo 2");
console.log(state);
/*
{
  todos: [
    { id: 1, title: 'Todo 1', completed: false },
    { id: 2, title: 'Todo 2', completed: false },
  ],
  stats: { all: 2, active: 2, completed: 0 }
}
*/

AddTodo(3, "Todo 3");
console.log(state);
/*
{
  todos: [
    { id: 1, title: 'Todo 1', completed: false },
    { id: 2, title: 'Todo 2', completed: false },
    { id: 3, title: 'Todo 3', completed: false }
  ],
  stats: { all: 3, active: 3, completed: 0 }
}
*/

ToggleTodo(3);
console.log(state);
/*
{
  todos: [
    { id: 1, title: 'Todo 1', completed: false },
    { id: 2, title: 'Todo 2', completed: false },
    { id: 3, title: 'Todo 3', completed: true }
  ],
  stats: { all: 3, active: 2, completed: 1 }
}
*/

API References

imh(obj, mutation/mutations)

imh(1, imh.add(1));
// => 2

// using val() to tell imh that is literal value (not mutation)
imh({ username: "admin", password: "admin" }, [
  imh.prop("password", imh.val("123456")),
  imh.prop("updatedOn", imh.val(Date.now())),
]);

// using custom mutation (a pure function that returns new value)
imh({ username: "admin", password: "admin" }, [
  imh.prop("password", () => "123456"),
  imh.prop("updatedOn", () => Date.now()),
]);

// using set() to update object property
imh({ username: "admin", password: "admin" }, [
  imh.set("password", "123456"),
  imh.set("updatedOn", Date.now()),
]);

imh(mutation/mutations)

Create imh wrapper function

const AddTen = imh(imh.add(10));
AddTen(1);
// => 11

Array

push(...items)

imh([1, 2, 3], imh.push(4, 5, 6));
// => [1, 2, 3, 4, 5, 6]

map(mutation/mutations)

const todos = [
  { id: 1, title: "Todo 1" },
  { id: 2, title: "Todo 2" },
];
imh(
  todos,
  imh.map((todo) => ({ ...todo, title: todo.title.toUpperCase() }))
);
// => [ { id: 1, title: "TODO 1" }, { id: 2, title: "TODO 2" } ]

imh(todos, imh.map(imh.prop("title", imh.lower())));
// => [ { id: 1, title: "todo 1" }, { id: 2, title: "todo 2" } ]

splice(index, length, ...newItems)

imh([1, 2, 3, 4, 5], imh.splice(2, 2));
// => [1, 2, 5]

imh([1, 2, 3, 4, 5], imh.splice(2, 2, 9, 10));
// => [1, 2, 9, 10, 5]

filter(predicate)

imh(
  [1, 2, 3, 4, 5],
  imh.filter((number) => number % 2 === 0)
);
// => [2, 4]

sort([compareFn])

imh([3, 2, 1], imh.sort());
// => [1, 2, 3]

imh(
  [{ name: "banana" }, { name: "apple" }, { name: "watermelon" }],
  imh.sort((a, b) => (a.name > b.name ? 1 : a.name < b.name ? -1 : 0))
);
// => [{ name: "apple" }, { name: "banana" }, { name: "watermelon" }]

orderBy([selector[, direction]])

// order by name ascending
imh(
  [{ name: "banana" }, { name: "apple" }, { name: "watermelon" }],
  imh.orderBy((item) => item.name)
);
// => [{ name: "apple" }, { name: "banana" }, { name: "watermelon" }]

// order by name descending
imh(
  [{ name: "banana" }, { name: "apple" }, { name: "watermelon" }],
  imh.orderBy((item) => item.name, -1)
);
// => [{ name: "watermelon" }, { name: "banana" }, { name: "apple" }]

swap(from, to)

imh([1, 2, 3], imh.swap(0, 2));
// => [3, 2, 1]

remove(...indices)

imh([1, 2, 3], imh.remove(0, 2));
// => [2]

clear()

imh([1, 2, 3], imh.clear());
// => []

pop()

imh([1, 2, 3], imh.pop());
// => [1, 2]

shift()

imh([1, 2, 3], imh.shift());
// => [2, 3]

unshift(...items)

imh([1, 2, 3], imh.unshift(-1, 0));
// => [-1, 0, 1, 2, 3]

reverse()

imh([1, 2, 3], imh.reverse());
// => [3, 2, 1]

Object

prop(name, mutation) & val(value)

Update current / nested object property

const model = { l1: { l2: { l3: { l4: 1 } } } };
imh(
  model,
  imh.prop("l1", imh.prop("l2", imh.prop("l3", imh.prop("l4", imh.val(2)))))
);

imh(
  model,
  imh.prop(
    "l1",
    imh.prop(
      "l2",
      imh.prop(
        "l3",
        imh.prop("l4", () => 2)
      )
    )
  )
);

imh(model, imh.prop("l1", imh.prop("l2", imh.prop("l3", imh.set("l4", 2)))));

imh(model, imh.prop(["l1", "l2", "l3", "l4"], imh.val(2)));

imh(
  model,
  imh.prop(["l1", "l2", "l3", "l4"], () => 2)
);

imh(model, imh.prop(["l1", "l2", "l3"], imh.set("l4", 2)));

set(key, value)

imh({ name: "Peter" }, imh.set("name", "Spider Man"));
// => { name: 'Spider Man' }

imh([1, 2, 3], imh.set(1, 4));
// => [1, 4, 3]

unset(...keys)

imh({ prop1: 1, prop2: 2, prop3: 3 }, imh.unset("prop1", "prop2"));
// => { prop3: 3 }

imh([1, 2, 3], imh.unset(1, 2));
// => [1, undefined, undefined]

merge(...values)

imh({ p1: 1, p2: 2 }, imh.merge({ p1: 1, p2: 2 }));
// => { p1: 1, p2: 2 } nothing to change

imh({ p1: 1, p2: 2 }, imh.merge({ p1: 1 }, { p2: 2 }));
// => { p1: 1, p2: 2 } nothing to change

imh({ p1: 1, p2: 2 }, imh.merge({ p1: 5 }, { p1: 1 }));
// => { p1: 1, p2: 2 } nothing to change

imh({ p1: 1, p2: 2 }, imh.merge({ p3: 3 }));
// => { p1: 1, p2: 2, p3: 3 }

String

replace(findWhat, replaceWith)

imh("banana, apple, watermelon, banana", imh.replace("banana", "orange"));
// => 'orange, apple, watermelon, banana`

imh("banana, apple, watermelon, banana", imh.replace("banana", /orange/g));
// => 'orange, apple, watermelon, orange`

upper()

imh("Oop!!!", imh.upper());
// => OOP!!!

lower()

imh("Oop!!!", imh.lower());
// => oop!!!

Misc

add()

imh(1, imh.add(9));
// => 10

imh(
  new Date(2000, 1, 1),
  img.add({
    years: 1,
    months: 1,
    days: 1,
    hours: 12,
    minutes: 12,
    seconds: 12,
    milliseconds: 900,
  })
);
// => 2001/02/02 12:12:12:900

imh(
  // unix timestamp
  new Date(2000, 1, 1).getTime(),
  img.add({
    years: 1,
    months: 1,
    days: 1,
    hours: 12,
    minutes: 12,
    seconds: 12,
    milliseconds: 900,
  })
);
// => unix timestamp

toggle()

imh({ completed: false }, imh.prop("completed", imh.toggle()));
// => { completed: true }

result(callback)

Get result of last mutation. It is often used with splice() / pop() / shift()

let state = {
  sourceList: ["item 1", "item 2"],
  destList: ["item 3", "item 4"],
};

function move(index, count) {
  state = imh(state, [
    // remove some from sourceList
    imh.prop("sourceList", imh.splice(index, count)),
    // append to destList
    imh.result((result) => imh.prop("destList", imh.push(...result))),
  ]);
}