Arrays & Objects
These helpers operate on arrays and objects: building combinations, compiling conditional class strings, and reading or writing deeply nested values with a dot path.
import { crossJoin, toCssClasses, get, set } from '@0x26e/utils'crossJoin
Returns the Cartesian product of the given arrays — a list of every possible combination where each combination contains one element from each input array.
function crossJoin<T = number | string>(
...arrays: (number[] | string[])[]
): T[][]Parameters
| Parameter | Type | Description |
|---|---|---|
...arrays | (number[] | string[])[] | One or more arrays to combine. |
Returns T[][] — an array of combinations, where each inner array holds one element from each
input array.
Examples
Two arrays:
const result = crossJoin([1, 2], ['a', 'b'])
console.log(result)
// [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]Three arrays — the result grows multiplicatively (2 × 2 × 2 = 8 combinations):
const result = crossJoin([1, 2], ['a', 'b'], ['x', 'y'])
console.log(result)
// [
// [1, 'a', 'x'], [1, 'a', 'y'],
// [1, 'b', 'x'], [1, 'b', 'y'],
// [2, 'a', 'x'], [2, 'a', 'y'],
// [2, 'b', 'x'], [2, 'b', 'y'],
// ]A practical case — generating product variants from sizes, colors, and shapes:
const variants = crossJoin(['S', 'M'], ['White', 'Black'], ['Round'])
// [
// ['S', 'White', 'Round'],
// ['S', 'Black', 'Round'],
// ['M', 'White', 'Round'],
// ['M', 'Black', 'Round'],
// ]Order is preserved
Combinations are produced in a stable order: the first array varies slowest and the last array varies fastest. Each inner array keeps the same positional order as the inputs.
toCssClasses
Conditionally compiles a CSS class string from an array of entries. Each entry is either a plain string (always included) or an object whose keys are class names and whose values are boolean expressions. A key is included only when its value is truthy.
function toCssClasses(
classes: Array<string | Record<string | number, boolean>>,
): stringParameters
| Parameter | Type | Description |
|---|---|---|
classes | Array<string | Record<string | number, boolean>> | A list of strings and condition objects. |
Returns string — the space-joined class string, with empty entries removed.
Examples
Mixing a static class with conditional ones:
const isActive = false
const hasError = true
const classes = toCssClasses(['p-4', { 'font-bold': isActive, 'bg-red': hasError }])
console.log(classes) // 'p-4 bg-red'When no condition matches, the result is an empty string:
toCssClasses([{ 'font-bold': false, 'bg-red': false }]) // ''Numeric keys are always included
If an object key is numeric, it is always added to the output regardless of its value. This matches Laravel's behavior — a numeric key signals "always render this class".
toCssClasses(['p-4', { 1: true }]) // 'p-4 1'
toCssClasses(['p-4', { 'font-bold': false, 'bg-red': true, 1: true }]) // 'p-4 1 bg-red'get
Retrieves a value from a deeply nested object using dot notation. If any segment of the path is
missing — or the resolved value is undefined — it returns the provided default instead.
function get<T>(
obj: Record<string, any>,
path: string,
defaultValue?: T,
): T | undefinedParameters
| Parameter | Type | Description |
|---|---|---|
obj | Record<string, any> | The object to read from. |
path | string | A dot-notation path, e.g. 'products.desk.price'. |
defaultValue | T (optional) | Returned when the path is not found. Defaults to undefined. |
Returns T | undefined — the value at the path, or the default value.
Examples
Reading a nested value, with a default for the miss:
const obj = { products: { desk: { price: 100 } } }
get(obj, 'products.desk.price', 0) // 100
get(obj, 'products.table.price', 0) // 0 (path not found → default)Array indices work as path segments too:
const data = { items: [{ id: 1 }, { id: 2 }] }
get(data, 'items.1.id', 0) // 2It traverses safely through null and missing branches instead of throwing:
get({ products: null }, 'products.desk.price', 0) // 0
get({}, 'products.desk.price', 0) // 0undefined leaf values fall back to the default
If the path resolves to a value that is literally undefined, get returns the default. With no
default supplied, it returns undefined.
get({ products: { desk: { price: undefined } } }, 'products.desk.price') // undefinedset
Sets a value within a deeply nested object using dot notation. Any missing intermediate objects are created along the way. It mutates the object in place and also returns it.
function set(
obj: Record<string, any>,
path: string,
value: any,
): Record<string, any>Parameters
| Parameter | Type | Description |
|---|---|---|
obj | Record<string, any> | The object to write to. It is mutated in place. |
path | string | A dot-notation path to the target location. |
value | any | The value to assign at the path. |
Returns Record<string, any> — the same obj, now updated.
Examples
Overwriting an existing nested value:
const obj = { products: { desk: { price: 100 } } }
set(obj, 'products.desk.price', 200)
console.log(obj) // { products: { desk: { price: 200 } } }Missing intermediate objects are created automatically:
const obj = { products: {} }
set(obj, 'products.desk.price', 200)
console.log(obj) // { products: { desk: { price: 200 } } }It also works on an empty object, and replaces non-object branches (like null) on the way:
set({}, 'products.desk.price', 200) // { products: { desk: { price: 200 } } }
set({ products: null }, 'products.desk.price', 200) // { products: { desk: { price: 200 } } }Numeric segments index into arrays:
const obj = { items: [{ id: 1 }, { id: 2 }] }
set(obj, 'items.1.id', 3)
console.log(obj.items[1].id) // 3set mutates its argument
set modifies the object you pass in — it does not return a copy. The return value is the same
reference, provided as a convenience for chaining. If you need to preserve the original, clone it
first.