CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-seamless-immutable

Immutable data structures for JavaScript which are backwards-compatible with normal JS Arrays and Objects.

Pending
Overview
Eval results
Files

nested-operations.mddocs/

Nested Operations

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.

Capabilities

setIn

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"}}}

getIn

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: 6

updateIn

Updates 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)

update (Single-level)

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);

Path Resolution

Automatic Structure Creation

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"}}]}

Path Validation

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 path

Working with Mixed Structures

Nested 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"
);

Instance vs Static Methods

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

docs

array-operations.md

core.md

index.md

nested-operations.md

object-operations.md

tile.json