UNPKG

imh

Version:

The extremely fast immutable helper

470 lines (371 loc) 9.58 kB
# 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 1. If you need a complete, well-tested, rock-solid library and don't mind using a non-native API for reads: use ImmutableJS 1. If you value using plain arrays/objects above other considerations, use **imh** 1. If your typical use cases involve much more reading than writing, use **imh** as well 1. If you do a lot of writes, updating items in long arrays or attributes in fat objects, use ImmutableJS ## Usage ```jsx 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)** ```jsx 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 ```jsx const AddTen = imh(imh.add(10)); AddTen(1); // => 11 ``` ## Array **push(...items)** ```jsx imh([1, 2, 3], imh.push(4, 5, 6)); // => [1, 2, 3, 4, 5, 6] ``` **map(mutation/mutations)** ```jsx 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)** ```jsx 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)** ```jsx imh( [1, 2, 3, 4, 5], imh.filter((number) => number % 2 === 0) ); // => [2, 4] ``` **sort([compareFn])** ```jsx 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]])** ```jsx // 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)** ```jsx imh([1, 2, 3], imh.swap(0, 2)); // => [3, 2, 1] ``` **remove(...indices)** ```jsx imh([1, 2, 3], imh.remove(0, 2)); // => [2] ``` **clear()** ```jsx imh([1, 2, 3], imh.clear()); // => [] ``` **pop()** ```jsx imh([1, 2, 3], imh.pop()); // => [1, 2] ``` **shift()** ```jsx imh([1, 2, 3], imh.shift()); // => [2, 3] ``` **unshift(...items)** ```jsx imh([1, 2, 3], imh.unshift(-1, 0)); // => [-1, 0, 1, 2, 3] ``` **reverse()** ```jsx imh([1, 2, 3], imh.reverse()); // => [3, 2, 1] ``` ## Object **prop(name, mutation) & val(value)** Update current / nested object property ```jsx 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)** ```jsx imh({ name: "Peter" }, imh.set("name", "Spider Man")); // => { name: 'Spider Man' } imh([1, 2, 3], imh.set(1, 4)); // => [1, 4, 3] ``` **unset(...keys)** ```jsx 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)** ```jsx 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)** ```jsx 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()** ```jsx imh("Oop!!!", imh.upper()); // => OOP!!! ``` **lower()** ```jsx imh("Oop!!!", imh.lower()); // => oop!!! ``` ## Misc **add()** ```jsx 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()** ```jsx 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() ```jsx 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))), ]); } ```