Immutable data structures for JavaScript which are backwards-compatible with normal JS Arrays and Objects.
—
Methods for working with nested data structures using path-based access patterns. These operations work on both objects and arrays using array paths to specify nested locations.
Sets a nested property using a path array, creating intermediate objects/arrays as needed.
/**
* Set nested property using path array
* @param {Object|Array} obj - Target object or array
* @param {Array} path - Array of keys/indices representing the path to the nested property
* @param {*} value - Value to set at the nested location
* @param {Object} [config] - Configuration options
* @param {boolean} [config.deep] - Deep merge if setting an object value
* @returns {Object|Array} New immutable structure with nested property set
*/
function setIn(obj, path, value, config);Usage Examples:
const Immutable = require("seamless-immutable");
// Setting in nested objects
const obj = Immutable({type: {main: "parrot", sub: "Norwegian Blue"}, status: "alive"});
const updated = obj.setIn(["type", "sub"], "Norwegian Ridgeback");
// Result: {type: {main: "parrot", sub: "Norwegian Ridgeback"}, status: "alive"}
// Setting in nested arrays
const arr = Immutable([[1, 2], [3, 4]]);
const updatedArr = arr.setIn([1, 0], 99);
// Result: [[1, 2], [99, 4]]
// Creating intermediate structures
const empty = Immutable({});
const nested = empty.setIn(["user", "profile", "name"], "Alice");
// Result: {user: {profile: {name: "Alice"}}}
// Mixed object/array paths
const complex = Immutable({users: [{name: "Alice"}, {name: "Bob"}]});
const updatedComplex = complex.setIn(["users", 0, "age"], 30);
// Result: {users: [{name: "Alice", age: 30}, {name: "Bob"}]}
// Deep merge when setting objects
const deepObj = Immutable({user: {name: "Alice", settings: {theme: "dark"}}});
const merged = deepObj.setIn(["user"], {age: 30}, {deep: true});
// Result: {user: {name: "Alice", age: 30, settings: {theme: "dark"}}}Gets a nested property value using a path array, with optional default value.
/**
* Get nested property using path array
* @param {Object|Array} obj - Target object or array
* @param {Array} path - Array of keys/indices representing the path to the nested property
* @param {*} [defaultValue] - Default value returned if path is not found
* @returns {*} Value at the specified path, or defaultValue if not found
*/
function getIn(obj, path, defaultValue);Usage Examples:
const Immutable = require("seamless-immutable");
const obj = Immutable({
type: {main: "parrot", subtype: "Norwegian Blue"},
status: "alive",
users: [{name: "Alice", age: 30}, {name: "Bob", age: 25}]
});
// Get nested object property
const subtype = obj.getIn(["type", "subtype"]);
// Result: "Norwegian Blue"
// Get from nested array
const firstUserName = obj.getIn(["users", 0, "name"]);
// Result: "Alice"
// Path not found - returns undefined
const missing = obj.getIn(["type", "missing"]);
// Result: undefined
// Path not found with default value
const withDefault = obj.getIn(["type", "class"], "Aves");
// Result: "Aves"
// Deeply nested path
const deepNested = Immutable({a: {b: {c: {d: "found"}}}});
const value = deepNested.getIn(["a", "b", "c", "d"]);
// Result: "found"
// Array indices in path
const matrix = Immutable([[1, 2, 3], [4, 5, 6]]);
const element = matrix.getIn([1, 2]);
// Result: 6Updates a nested property using an updater function and path array.
/**
* Update nested property using updater function and path array
* @param {Object|Array} obj - Target object or array
* @param {Array} path - Array of keys/indices representing the path to the nested property
* @param {Function} updater - Function that receives current value and returns new value
* @param {...*} [args] - Additional arguments passed to the updater function
* @returns {Object|Array} New immutable structure with updated nested property
*/
function updateIn(obj, path, updater, ...args);Usage Examples:
const Immutable = require("seamless-immutable");
// Simple numeric update
function add(x, y) { return x + y; }
const obj = Immutable({foo: {bar: 1}});
const incremented = obj.updateIn(["foo", "bar"], add, 10);
// Result: {foo: {bar: 11}}
// String manipulation
const user = Immutable({profile: {name: "alice"}});
const capitalized = user.updateIn(["profile", "name"], name => name.toUpperCase());
// Result: {profile: {name: "ALICE"}}
// Array manipulation
const data = Immutable({items: {list: [1, 2, 3]}});
const withNewItem = data.updateIn(["items", "list"], list => list.concat(4));
// Result: {items: {list: [1, 2, 3, 4]}}
// Complex nested structure
const state = Immutable({
users: [
{id: 1, name: "Alice", scores: [10, 20]},
{id: 2, name: "Bob", scores: [15, 25]}
]
});
// Update nested array element
const updatedScores = state.updateIn(["users", 0, "scores"], scores =>
scores.map(score => score * 2)
);
// Result: users[0].scores becomes [20, 40]
// Update with multiple arguments
function multiply(current, factor, bonus) {
return current * factor + bonus;
}
const calculated = obj.updateIn(["foo", "bar"], multiply, 3, 5);
// Result: {foo: {bar: 8}} (1 * 3 + 5)Updates a property at the current level using an updater function.
/**
* Update a property using an updater function
* @param {Object|Array} obj - Target object or array
* @param {string|number} property - Property key or array index to update
* @param {Function} updater - Function that receives current value and returns new value
* @param {...*} [args] - Additional arguments passed to the updater function
* @returns {Object|Array} New immutable structure with updated property
*/
function update(obj, property, updater, ...args);Usage Examples:
const Immutable = require("seamless-immutable");
// Object property update
function inc(x) { return x + 1; }
const obj = Immutable({foo: 1, bar: 2});
const incremented = obj.update("foo", inc);
// Result: {foo: 2, bar: 2}
// Array element update
const arr = Immutable([10, 20, 30]);
const doubled = arr.update(1, x => x * 2);
// Result: [10, 40, 30]
// Update with additional arguments
function add(current, amount) { return current + amount; }
const added = obj.update("foo", add, 5);
// Result: {foo: 6, bar: 2}
// Conditional updates
const conditionalUpdate = obj.update("foo", (current, threshold) =>
current > threshold ? current * 2 : current
, 0.5);When using setIn, intermediate structures are automatically created based on the next key in the path:
const Immutable = require("seamless-immutable");
const empty = Immutable({});
// Creates nested objects
const result1 = empty.setIn(["a", "b", "c"], "value");
// Result: {a: {b: {c: "value"}}}
// Creates array when next key is numeric
const result2 = empty.setIn(["users", 0, "name"], "Alice");
// Result: {users: [{name: "Alice"}]}
// Mixed creation
const result3 = empty.setIn(["data", 0, "items", "first"], "value");
// Result: {data: [{items: {first: "value"}}]}Paths must be arrays with at least one element:
const obj = Immutable({a: 1});
// Valid paths
obj.setIn(["a"], 2); // ✓
obj.setIn(["b", "c"], 3); // ✓
obj.setIn([0, "name"], "test"); // ✓
// Invalid paths - throw TypeError
obj.setIn([], "value"); // ✗ Empty path
obj.setIn("a", "value"); // ✗ Not an array
obj.setIn(null, "value"); // ✗ Null pathNested operations handle complex data structures with mixed objects and arrays:
const Immutable = require("seamless-immutable");
const complexData = Immutable({
users: [
{
id: 1,
profile: {
name: "Alice",
addresses: [
{type: "home", city: "NYC"},
{type: "work", city: "SF"}
]
}
}
],
settings: {
theme: "dark",
features: ["feature1", "feature2"]
}
});
// Deep nested update
const updated = complexData.updateIn(
["users", 0, "profile", "addresses", 0, "city"],
city => city.toUpperCase()
);
// Get deep nested value
const workCity = complexData.getIn(["users", 0, "profile", "addresses", 1, "city"]);
// Set deep nested value
const withNewFeature = complexData.setIn(
["settings", "features", 2],
"feature3"
);All nested operation methods are available in both instance and static forms:
const Immutable = require("seamless-immutable");
const obj = Immutable({user: {name: "Alice"}});
// Instance methods (default API)
const set1 = obj.setIn(["user", "age"], 30);
const get1 = obj.getIn(["user", "name"]);
const updated1 = obj.updateIn(["user", "name"], name => name.toUpperCase());
// Static methods (static API)
const ImmutableS = require("seamless-immutable").static;
const set2 = ImmutableS.setIn(obj, ["user", "age"], 30);
const get2 = ImmutableS.getIn(obj, ["user", "name"]);
const updated2 = ImmutableS.updateIn(obj, ["user", "name"], name => name.toUpperCase());The static API is recommended when you want to avoid method name pollution or work with a consistent functional programming style.
Install with Tessl CLI
npx tessl i tessl/npm-seamless-immutable