Skip to content

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

ParameterTypeDescription
...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>>,
): string

Parameters

ParameterTypeDescription
classesArray<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 | undefined

Parameters

ParameterTypeDescription
objRecord<string, any>The object to read from.
pathstringA dot-notation path, e.g. 'products.desk.price'.
defaultValueT (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) // 2

It traverses safely through null and missing branches instead of throwing:

get({ products: null }, 'products.desk.price', 0) // 0
get({}, 'products.desk.price', 0) // 0

undefined 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') // undefined

set

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

ParameterTypeDescription
objRecord<string, any>The object to write to. It is mutated in place.
pathstringA dot-notation path to the target location.
valueanyThe 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) // 3

set 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.

js-utils — A TypeScript utility collection similar to Laravel helpers. Released under the MIT license.