Immutable data structures for JavaScript which are backwards-compatible with normal JS Arrays and Objects.
—
Methods for working with immutable arrays including functional operations, transformations, and element manipulation.
Maps over array elements and flattens the results, similar to map followed by flatten.
/**
* Maps and flattens array elements
* @param {Array} array - Source array to process
* @param {Function} iterator - Function that processes each element and returns a value or array
* @returns {Array} New immutable array with mapped and flattened results
*/
function flatMap(array, iterator);Usage Examples:
const Immutable = require("seamless-immutable");
const array = Immutable(["here", "we", "go"]);
const repeated = array.flatMap(function(str) {
return [str, str, str];
});
// Result: ["here", "here", "here", "we", "we", "we", "go", "go", "go"]
// Filter out numbers by returning empty arrays
const mixed = Immutable(["drop the numbers!", 3, 2, 1, 0, null, undefined]);
const filtered = mixed.flatMap(function(value) {
if (typeof value === "number") {
return []; // Empty array removes the element
} else {
return value; // Non-arrays are treated as single elements
}
});
// Result: ["drop the numbers!", null, undefined]
// Split strings into characters
const words = Immutable(["hello", "world"]);
const chars = words.flatMap(word => word.split(''));
// Result: ["h", "e", "l", "l", "o", "w", "o", "r", "l", "d"]Converts an array to an object using key-value pairs, either from a provided iterator or assuming the array contains pairs.
/**
* Convert array to object using key-value pairs
* @param {Array} array - Source array to convert
* @param {Function} [iterator] - Function that returns [key, value] pairs for each element
* @returns {Object} New immutable object created from the array
*/
function asObject(array, iterator);Usage Examples:
const Immutable = require("seamless-immutable");
// With iterator function
const array = Immutable(["hey", "you"]);
const obj = array.asObject(function(str) {
return [str, str.toUpperCase()];
});
// Result: {hey: "HEY", you: "YOU"}
// Array already contains key-value pairs
const pairs = Immutable([["name", "Alice"], ["age", 30]]);
const person = pairs.asObject();
// Result: {name: "Alice", age: 30}
// Using with objects to extract keys
const users = Immutable([
{id: 1, name: "Alice"},
{id: 2, name: "Bob"}
]);
const usersById = users.asObject(user => [user.id, user]);
// Result: {1: {id: 1, name: "Alice"}, 2: {id: 2, name: "Bob"}}
// Iterator receives index as second parameter
const indexed = Immutable(["a", "b", "c"]).asObject((value, index) => [index, value]);
// Result: {0: "a", 1: "b", 2: "c"}Sets an element at a specific index in an array.
/**
* Set element at specific index in an array
* @param {Array} array - Source array
* @param {number} idx - Index to set (can be beyond current length)
* @param {*} value - Value to set at the index
* @param {Object} [config] - Configuration options
* @param {boolean} [config.deep] - Deep merge if setting an object value
* @returns {Array} New immutable array with element set
*/
function set(array, idx, value, config);Usage Examples:
const Immutable = require("seamless-immutable");
const arr = Immutable([1, 2, 3]);
// Set existing index
const updated = arr.set(1, 99);
// Result: [1, 99, 3]
// Set beyond current length - fills with undefined
const extended = arr.set(5, "new");
// Result: [1, 2, 3, undefined, undefined, "new"]
// Deep merge when setting object values
const objArr = Immutable([{name: "Alice"}, {name: "Bob"}]);
const merged = objArr.set(0, {age: 30}, {deep: true});
// Result: [{name: "Alice", age: 30}, {name: "Bob"}]
// Using static method
const staticSet = Immutable.set(arr, 2, "replaced");
// Result: [1, 2, "replaced"]Converts an immutable array to a mutable copy.
/**
* Convert immutable array to mutable copy
* @param {Array} array - Immutable array to convert
* @param {Object} [options] - Conversion options
* @param {boolean} [options.deep] - Recursively convert nested immutable structures
* @returns {Array} Mutable copy of the array
*/
function asMutable(array, options);Usage Examples:
const Immutable = require("seamless-immutable");
const array = Immutable(["hello", "world"]);
const mutableArray = array.asMutable();
// Now can use mutating methods
mutableArray.push("!!!");
console.log(mutableArray); // ["hello", "world", "!!!"]
// Deep conversion for nested structures
const nested = Immutable([
{name: "Alice", hobbies: ["reading"]},
{name: "Bob", hobbies: ["coding"]}
]);
const shallowMutable = nested.asMutable();
// shallowMutable[0].name = "Charlie"; // Would still throw - nested objects still immutable
const deepMutable = nested.asMutable({deep: true});
deepMutable[0].name = "Charlie"; // Works - all nested structures are now mutable
deepMutable[0].hobbies.push("gaming"); // Also worksAll standard non-mutating array methods are enhanced to return immutable arrays:
/**
* Enhanced array methods that return immutable results
*/
function map(callback, thisArg);
function filter(callback, thisArg);
function slice(start, end);
function concat(...arrays);
function reduce(callback, initialValue);
function reduceRight(callback, initialValue);Usage Examples:
const Immutable = require("seamless-immutable");
const numbers = Immutable([1, 2, 3, 4, 5]);
// All return immutable arrays
const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
const firstThree = numbers.slice(0, 3);
const extended = numbers.concat([6, 7, 8]);
console.log(Immutable.isImmutable(doubled)); // true
console.log(Immutable.isImmutable(evens)); // true
console.log(Immutable.isImmutable(firstThree)); // true
console.log(Immutable.isImmutable(extended)); // true
// Chaining works as expected
const result = numbers
.map(n => n * 2)
.filter(n => n > 4)
.slice(0, 2);
// Result: [6, 8] (immutable)Returns an immutable result for object keys.
/**
* Get array indices as an immutable array
* @returns {Array} Immutable array of indices
*/
function keys();These methods throw ImmutableError when called on immutable arrays:
// These methods throw ImmutableError:
function push(...elements); // Use concat() instead
function pop(); // Use slice(0, -1) instead
function shift(); // Use slice(1) instead
function unshift(...elements); // Use concat(newElements, array) instead
function splice(start, deleteCount, ...items); // Use slice() and concat() instead
function sort(compareFn); // Use slice().sort() on mutable copy instead
function reverse(); // Use slice().reverse() on mutable copy insteadAlternative Patterns:
const Immutable = require("seamless-immutable");
const arr = Immutable([1, 2, 3]);
// Instead of mutating methods, use immutable alternatives:
// Instead of push(4)
const withFour = arr.concat([4]);
// Instead of pop()
const withoutLast = arr.slice(0, -1);
// Instead of shift()
const withoutFirst = arr.slice(1);
// Instead of unshift(0)
const withZero = [0].concat(arr);
// Instead of sort()
const sorted = arr.asMutable().sort(); // Convert to mutable first
// Or: const sorted = Immutable(arr.slice().sort());
// Instead of reverse()
const reversed = Immutable(arr.slice().reverse());All array methods are available in both instance and static forms:
const Immutable = require("seamless-immutable");
const arr = Immutable([1, 2, 3]);
// Instance methods (default API)
const mapped1 = arr.flatMap(x => [x, x]);
const obj1 = arr.asObject((val, idx) => [idx, val]);
const updated1 = arr.set(1, 99);
// Static methods (static API)
const ImmutableS = require("seamless-immutable").static;
const mapped2 = ImmutableS.flatMap(arr, x => [x, x]);
const obj2 = ImmutableS.asObject(arr, (val, idx) => [idx, val]);
const updated2 = ImmutableS.set(arr, 1, 99);The static API is recommended when you want to avoid method name pollution or potential conflicts with existing array methods.
Install with Tessl CLI
npx tessl i tessl/npm-seamless-immutable