Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

imh

linq2js6ISC0.0.6TypeScript support: included

The extremely fast immutable helper

functional, flux, immutable, data, stateless, javascript, redux, clone, copy, assign, typescript, state, clone, deepcopy, assign, copy, data, datastructure, react, angular, vue

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))),
  ]);
}