receta

Collection

31 exports from @ontologyio/receta/collection.

Everything exported from @ontologyio/receta/collection. New here? Start with the Collection guide.

Functions

batchBy

Groups consecutive items in an array based on a predicate or grouping function.

Unlike groupBy which groups ALL matching items together, batchBy only groups consecutive items that produce the same grouping key. Useful for identifying runs, sequences, or consecutive states in time-series data.

Data-first
function batchBy<T, K extends string | number>(items: readonly T[], fn: (item: T) => K): readonly (readonly T[])[]
Data-last
function batchBy<T, K extends string | number>(fn: (item: T) => K): (items: readonly T[]) => readonly (readonly T[])[]
Example
// Data-first: Group consecutive same values
batchBy([1, 1, 2, 2, 2, 3, 1, 1], x => x)
// => [[1, 1], [2, 2, 2], [3], [1, 1]]

// Group consecutive dates by day
const events = [
  { timestamp: '2024-01-01T10:00:00Z', event: 'login' },
  { timestamp: '2024-01-01T11:00:00Z', event: 'click' },
  { timestamp: '2024-01-02T09:00:00Z', event: 'login' },
  { timestamp: '2024-01-02T10:00:00Z', event: 'logout' },
]

batchBy(events, e => e.timestamp.split('T')[0])
// => [
//   [{ timestamp: '2024-01-01T10:00:00Z', ... }, { timestamp: '2024-01-01T11:00:00Z', ... }],
//   [{ timestamp: '2024-01-02T09:00:00Z', ... }, { timestamp: '2024-01-02T10:00:00Z', ... }]
// ]

// Group consecutive status changes
const tasks = [
  { id: 1, status: 'pending' },
  { id: 2, status: 'pending' },
  { id: 3, status: 'done' },
  { id: 4, status: 'done' },
  { id: 5, status: 'pending' },
]

batchBy(tasks, t => t.status)
// => [
//   [{ id: 1, status: 'pending' }, { id: 2, status: 'pending' }],
//   [{ id: 3, status: 'done' }, { id: 4, status: 'done' }],
//   [{ id: 5, status: 'pending' }]
// ]

// Data-last (in pipe)
pipe(
  [1, 1, 2, 2, 2, 3, 1, 1],
  batchBy(x => x)
)
// => [[1, 1], [2, 2, 2], [3], [1, 1]]

See also groupBy, chunk.

cartesianProduct

Generates the Cartesian product of multiple arrays.

Creates all possible combinations by taking one element from each input array. Useful for generating test matrices, A/B test variants, configuration combinations, or any scenario requiring all possible combinations.

Type-safe for 2-5 arrays with specific tuple types, falls back to generic array for more than 5 arrays.

Overload 1
function cartesianProduct<A, B>(arr1: readonly A[], arr2: readonly B[]): readonly [A, B][]
Overload 2
function cartesianProduct<A, B, C>(arr1: readonly A[], arr2: readonly B[], arr3: readonly C[]): readonly [A, B, C][]
Overload 3
function cartesianProduct<A, B, C, D>(arr1: readonly A[], arr2: readonly B[], arr3: readonly C[], arr4: readonly D[]): readonly [A, B, C, D][]
Overload 4
function cartesianProduct<A, B, C, D, E>(arr1: readonly A[], arr2: readonly B[], arr3: readonly C[], arr4: readonly D[], arr5: readonly E[]): readonly [A, B, C, D, E][]
Overload 5
function cartesianProduct<T>(...arrays: readonly (readonly T[])[]): readonly T[][]
Example
// Two arrays
cartesianProduct(['a', 'b'], [1, 2, 3])
// => [['a', 1], ['a', 2], ['a', 3], ['b', 1], ['b', 2], ['b', 3]]

// Three arrays (A/B test variants)
const sizes = ['S', 'M', 'L']
const colors = ['red', 'blue']
const materials = ['cotton', 'polyester']

cartesianProduct(sizes, colors, materials)
// => [
//   ['S', 'red', 'cotton'],
//   ['S', 'red', 'polyester'],
//   ['S', 'blue', 'cotton'],
//   ['S', 'blue', 'polyester'],
//   ['M', 'red', 'cotton'],
//   ...
// ]

// Test matrix generation
const browsers = ['chrome', 'firefox', 'safari']
const platforms = ['mac', 'windows', 'linux']
const versions = ['v1', 'v2']

cartesianProduct(browsers, platforms, versions)
// => [
//   ['chrome', 'mac', 'v1'],
//   ['chrome', 'mac', 'v2'],
//   ['chrome', 'windows', 'v1'],
//   ...
// ]

// Configuration combinations
pipe(
  cartesianProduct(['dev', 'prod'], ['us', 'eu'], ['v1', 'v2']),
  R.map(([env, region, version]) => ({
    env,
    region,
    version,
    url: `https://${env}-${region}.example.com/${version}`
  }))
)

See also zip.

diff

Compares two collections and returns added, updated, removed, and unchanged items.

Useful for syncing data, detecting changes, and implementing optimistic updates.

Data-first
function diff<T, TId extends string | number>(oldItems: readonly T[], newItems: readonly T[], getId: (item: T) => TId, isEqual?: Comparator<T> | undefined): DiffResult<T>
Data-last
function diff<T, TId extends string | number>(newItems: readonly T[], getId: (item: T) => TId, isEqual?: Comparator<T> | undefined): (oldItems: readonly T[]) => DiffResult<T>
Example
// Data-first
const oldUsers = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' }
]
const newUsers = [
  { id: 1, name: 'Alicia' }, // updated
  { id: 3, name: 'Charlie' }  // added
]

diff(oldUsers, newUsers, (u) => u.id)
// => {
//   added: [{ id: 3, name: 'Charlie' }],
//   updated: [{ old: { id: 1, name: 'Alice' }, new: { id: 1, name: 'Alicia' } }],
//   removed: [{ id: 2, name: 'Bob' }],
//   unchanged: []
// }

// Data-last (in pipe)
pipe(
  oldUsers,
  diff(newUsers, (u) => u.id)
)

See also union, intersect.

flatten

Flattens a tree structure into a flat array with optional depth control and path tracking.

Unlike Remeda's flat() which flattens nested arrays, this function handles tree structures where each node contains children. Useful for hierarchical data like file systems, org charts, or nested comments.

Overload 1
function flatten<T>(items: readonly T[], config: FlattenConfig<T> & { includePath: true; }): readonly FlattenedItem<T>[]
Overload 2
function flatten<T>(items: readonly T[], config: FlattenConfig<T> & { includePath?: false | undefined; }): readonly T[]
Overload 3
function flatten<T>(items: readonly T[], config: FlattenConfig<T>): readonly T[] | readonly FlattenedItem<T>[]
Overload 4
function flatten<T>(config: FlattenConfig<T> & { includePath: true; }): (items: readonly T[]) => readonly FlattenedItem<T>[]
Overload 5
function flatten<T>(config: FlattenConfig<T> & { includePath?: false | undefined; }): (items: readonly T[]) => readonly T[]
Overload 6
function flatten<T>(config: FlattenConfig<T>): (items: readonly T[]) => readonly T[] | readonly FlattenedItem<T>[]
Example
// Data-first: File system tree
interface FileNode {
  name: string
  children?: FileNode[]
}

const tree: FileNode[] = [
  {
    name: 'src',
    children: [
      { name: 'index.ts' },
      { name: 'utils', children: [{ name: 'helper.ts' }] }
    ]
  }
]

flatten(tree, { getChildren: (n) => n.children })
// => [
//   { name: 'src', children: [...] },
//   { name: 'index.ts' },
//   { name: 'utils', children: [...] },
//   { name: 'helper.ts' }
// ]

// With path tracking
flatten(tree, {
  getChildren: (n) => n.children,
  includePath: true
})
// => [
//   { item: { name: 'src', ... }, path: [], depth: 0 },
//   { item: { name: 'index.ts' }, path: [{ name: 'src', ... }], depth: 1 },
//   { item: { name: 'utils', ... }, path: [{ name: 'src', ... }], depth: 1 },
//   { item: { name: 'helper.ts' }, path: [{ name: 'src', ... }, { name: 'utils', ... }], depth: 2 }
// ]

// With max depth
flatten(tree, {
  getChildren: (n) => n.children,
  maxDepth: 1
})
// => [
//   { name: 'src', children: [...] },
//   { name: 'index.ts' },
//   { name: 'utils', children: [...] }
// ]
// (stops at depth 1, doesn't include helper.ts)

// Data-last (in pipe)
pipe(
  tree,
  flatten({ getChildren: (n) => n.children })
)

See also nest.

groupByPath

Groups items by a nested object path.

Useful for grouping objects by nested properties like user.role or metadata.category.

Uses type-safe path access via Option pattern. Items with undefined/null paths are grouped under the 'undefined' key.

Data-first
function groupByPath<T>(items: readonly T[], path: string): Record<string, T[]>
Data-last
function groupByPath<T>(path: string): (items: readonly T[]) => Record<string, T[]>
Example
// Data-first
const users = [
  { name: 'Alice', profile: { role: 'admin' } },
  { name: 'Bob', profile: { role: 'user' } },
  { name: 'Charlie', profile: { role: 'admin' } }
]

groupByPath(users, 'profile.role')
// => {
//   admin: [
//     { name: 'Alice', profile: { role: 'admin' } },
//     { name: 'Charlie', profile: { role: 'admin' } }
//   ],
//   user: [{ name: 'Bob', profile: { role: 'user' } }]
// }

// Data-last (in pipe)
pipe(
  users,
  groupByPath('profile.role')
)

See also nest.

indexByUnique

Creates an index (map) from an array, ensuring keys are unique.

Returns a Result to handle duplicate key errors safely. Useful for normalizing data and creating lookup tables.

Data-first
function indexByUnique<T, TKey extends string | number>(items: readonly T[], getKey: (item: T) => TKey, config?: IndexConfig | undefined): Result<Record<TKey, T>, DuplicateKeyError>
Data-last
function indexByUnique<T, TKey extends string | number>(getKey: (item: T) => TKey, config?: IndexConfig | undefined): (items: readonly T[]) => Result<Record<TKey, T>, DuplicateKeyError>

Fails with DuplicateKeyError.

Example
// Data-first
const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' }
]

indexByUnique(users, (u) => u.id)
// => Ok({ 1: { id: 1, name: 'Alice' }, 2: { id: 2, name: 'Bob' } })

// Duplicate keys
const duplicates = [
  { id: 1, name: 'Alice' },
  { id: 1, name: 'Alicia' }
]

indexByUnique(duplicates, (u) => u.id)
// => Err(DuplicateKeyError: Duplicate key found: 1)

// Handle collisions with config
indexByUnique(duplicates, (u) => u.id, { onCollision: 'last' })
// => Ok({ 1: { id: 1, name: 'Alicia' } }) // keeps last

indexByUnique(duplicates, (u) => u.id, { onCollision: 'first' })
// => Ok({ 1: { id: 1, name: 'Alice' } }) // keeps first

// Data-last (in pipe)
pipe(
  users,
  indexByUnique((u) => u.id)
)

See also nest.

insertAt

Inserts one or more elements at a specific index (immutably).

Creates a new array with elements inserted at the specified index, shifting existing elements as needed. More ergonomic than splice for insertion-only operations. Built on top of Remeda's splice for consistency.

Data-first
function insertAt<T>(items: readonly T[], index: number, values: T | readonly T[]): readonly T[]
Data-last
function insertAt<T>(index: number, values: T | readonly T[]): (items: readonly T[]) => readonly T[]
Example
// Data-first: Insert single element
insertAt(['a', 'b', 'd', 'e'], 2, 'c')
// => ['a', 'b', 'c', 'd', 'e']

// Insert multiple elements
insertAt([1, 2, 5, 6], 2, [3, 4])
// => [1, 2, 3, 4, 5, 6]

// Insert at beginning
insertAt(['b', 'c'], 0, 'a')
// => ['a', 'b', 'c']

// Insert at end
insertAt(['a', 'b'], 2, 'c')
// => ['a', 'b', 'c']

// Negative index (from end)
insertAt(['a', 'b', 'd'], -1, 'c')
// => ['a', 'b', 'c', 'd']

// Add item to todo list at specific position
interface Todo {
  id: number
  text: string
}

const todos: Todo[] = [
  { id: 1, text: 'Buy milk' },
  { id: 3, text: 'Walk dog' },
]

insertAt(todos, 1, { id: 2, text: 'Read book' })
// => [
//   { id: 1, text: 'Buy milk' },
//   { id: 2, text: 'Read book' },
//   { id: 3, text: 'Walk dog' },
// ]

// Data-last (in pipe)
pipe(
  ['a', 'b', 'd', 'e'],
  insertAt(2, 'c')
)
// => ['a', 'b', 'c', 'd', 'e']

See also updateAt, removeAtIndex, moveIndex.

intersect

Finds elements that exist in both collections using a custom comparator.

Similar to Set intersection but works with objects and custom equality.

Data-first
function intersect<T>(items1: readonly T[], items2: readonly T[], isEqual?: Comparator<T> | undefined): T[]
Data-last
function intersect<T>(items2: readonly T[], isEqual?: Comparator<T> | undefined): (items1: readonly T[]) => T[]
Example
// Data-first - with primitive values
intersect([1, 2, 3], [2, 3, 4])
// => [2, 3]

// With objects and custom comparator
const assigned = [{ taskId: 1 }, { taskId: 2 }]
const completed = [{ taskId: 2 }, { taskId: 3 }]

intersect(assigned, completed, (a, b) => a.taskId === b.taskId)
// => [{ taskId: 2 }] // tasks that are both assigned and completed

// Data-last (in pipe)
pipe(
  assigned,
  intersect(completed, (a, b) => a.taskId === b.taskId)
)

See also union, diff.

moveIndex

Moves an element from one index to another (immutably).

Creates a new array with the element at fromIndex moved to toIndex, shifting other elements as needed. Useful for drag-and-drop reordering, priority adjustments, or any scenario requiring index-based repositioning.

Data-first
function moveIndex<T>(items: readonly T[], fromIndex: number, toIndex: number): readonly T[]
Data-last
function moveIndex(fromIndex: number, toIndex: number): <T>(items: readonly T[]) => readonly T[]
Example
// Data-first: Move element forward
moveIndex(['a', 'b', 'c', 'd', 'e'], 1, 3)
// => ['a', 'c', 'd', 'b', 'e']

// Move element backward
moveIndex(['a', 'b', 'c', 'd', 'e'], 3, 1)
// => ['a', 'd', 'b', 'c', 'e']

// Drag-and-drop reordering
interface Task {
  id: number
  title: string
  priority: number
}

const tasks: Task[] = [
  { id: 1, title: 'Task 1', priority: 1 },
  { id: 2, title: 'Task 2', priority: 2 },
  { id: 3, title: 'Task 3', priority: 3 },
]

// Move task from position 0 to position 2
moveIndex(tasks, 0, 2)
// => [
//   { id: 2, title: 'Task 2', priority: 2 },
//   { id: 3, title: 'Task 3', priority: 3 },
//   { id: 1, title: 'Task 1', priority: 1 },
// ]

// No-op if indices are same
moveIndex([1, 2, 3], 1, 1)
// => [1, 2, 3]

// Negative indices (from end)
moveIndex(['a', 'b', 'c', 'd'], -1, 0)
// => ['d', 'a', 'b', 'c']

// Data-last (in pipe)
pipe(
  ['a', 'b', 'c', 'd', 'e'],
  moveIndex(1, 3)
)
// => ['a', 'c', 'd', 'b', 'e']

See also insertAt, removeAtIndex, updateAt.

nest

Groups items hierarchically by multiple keys, creating a nested structure.

Useful for organizing data like comments grouped by user and post, or products grouped by category and subcategory.

Data-first
function nest<T>(items: readonly T[], keys: readonly (keyof T | ((item: T) => string | number))[]): NestedMap<T>
Data-last
function nest<T>(keys: readonly (keyof T | ((item: T) => string | number))[]): (items: readonly T[]) => NestedMap<T>
Example
// Data-first
const comments = [
  { userId: 1, postId: 10, text: 'Hello' },
  { userId: 1, postId: 10, text: 'World' },
  { userId: 2, postId: 20, text: 'Foo' }
]

nest(comments, ['userId', 'postId'])
// => {
//   1: {
//     10: [{ userId: 1, postId: 10, text: 'Hello' }, { userId: 1, postId: 10, text: 'World' }]
//   },
//   2: {
//     20: [{ userId: 2, postId: 20, text: 'Foo' }]
//   }
// }

// Data-last (in pipe)
pipe(
  comments,
  nest(['userId', 'postId'])
)

See also groupByPath.

paginate

Paginates an array using offset-based pagination.

Useful for implementing API pagination, infinite scroll, and data tables.

Data-first
function paginate<T>(items: readonly T[], config: PaginationConfig): PaginatedResult<T>
Data-last
function paginate<T>(config: PaginationConfig): (items: readonly T[]) => PaginatedResult<T>
Example
// Data-first
const users = Array.from({ length: 100 }, (_, i) => ({ id: i }))

paginate(users, { page: 1, pageSize: 20 })
// => {
//   items: [first 20 users],
//   page: 1,
//   pageSize: 20,
//   total: 100,
//   hasNext: true,
//   hasPrevious: false
// }

// Data-last (in pipe)
pipe(
  users,
  paginate({ page: 2, pageSize: 20 })
)

See also paginateCursor.

paginateCursor

Paginates an array using cursor-based pagination.

More efficient than offset pagination for large datasets and real-time data. Prevents issues with missing/duplicate items during pagination.

Data-first
function paginateCursor<T, TCursor>(items: readonly T[], getCursor: (item: T) => TCursor, config: CursorPaginationConfig<TCursor>): CursorPaginatedResult<T, TCursor>
Data-last
function paginateCursor<T, TCursor>(getCursor: (item: T) => TCursor, config: CursorPaginationConfig<TCursor>): (items: readonly T[]) => CursorPaginatedResult<T, TCursor>
Example
// Data-first
const messages = Array.from({ length: 25 }, (_, i) => ({ id: `msg_${i + 1}` }))

paginateCursor(messages, (m) => m.id, { limit: 10 })
// => {
//   items: [first 10 messages],
//   nextCursor: 'msg_10',
//   hasMore: true
// }

// Continue from cursor
paginateCursor(messages, (m) => m.id, { cursor: 'msg_10', limit: 10 })

// Data-last (in pipe)
pipe(
  messages,
  paginateCursor((m) => m.id, { limit: 10 })
)

See also paginate.

removeAtIndex

Removes an element at a specific index (immutably).

Creates a new array with the element at the specified index removed. More ergonomic than splice for single-element removal. Built on top of Remeda's splice for consistency.

Data-first
function removeAtIndex<T>(items: readonly T[], index: number): readonly T[]
Data-last
function removeAtIndex(index: number): <T>(items: readonly T[]) => readonly T[]
Example
// Data-first: Remove element
removeAtIndex(['a', 'b', 'c', 'd'], 2)
// => ['a', 'b', 'd']

// Remove at beginning
removeAtIndex([1, 2, 3], 0)
// => [2, 3]

// Remove at end
removeAtIndex([1, 2, 3], 2)
// => [1, 2]

// Negative index (from end)
removeAtIndex(['a', 'b', 'c', 'd'], -1)
// => ['a', 'b', 'c']

// Out of bounds returns original array
removeAtIndex([1, 2, 3], 10)
// => [1, 2, 3]

// Remove todo item
interface Todo {
  id: number
  text: string
}

const todos: Todo[] = [
  { id: 1, text: 'Buy milk' },
  { id: 2, text: 'Read book' },
  { id: 3, text: 'Walk dog' },
]

removeAtIndex(todos, 1)
// => [
//   { id: 1, text: 'Buy milk' },
//   { id: 3, text: 'Walk dog' },
// ]

// Data-last (in pipe)
pipe(
  ['a', 'b', 'c', 'd'],
  removeAtIndex(2)
)
// => ['a', 'b', 'd']

// Chain multiple removals
pipe(
  [1, 2, 3, 4, 5],
  removeAtIndex(0),  // [2, 3, 4, 5]
  removeAtIndex(-1)  // [2, 3, 4]
)
// => [2, 3, 4]

See also insertAt, updateAt, moveIndex.

symmetricDiff

Finds elements that exist in either collection but not both (symmetric difference).

Similar to XOR operation on sets.

Data-first
function symmetricDiff<T>(items1: readonly T[], items2: readonly T[], isEqual?: Comparator<T> | undefined): T[]
Data-last
function symmetricDiff<T>(items2: readonly T[], isEqual?: Comparator<T> | undefined): (items1: readonly T[]) => T[]
Example
// Data-first - with primitive values
symmetricDiff([1, 2, 3], [2, 3, 4])
// => [1, 4] // 1 only in first, 4 only in second

// With objects
const planned = [{ id: 1 }, { id: 2 }]
const actual = [{ id: 2 }, { id: 3 }]

symmetricDiff(planned, actual, (a, b) => a.id === b.id)
// => [{ id: 1 }, { id: 3 }] // id 1 not executed, id 3 not planned

// Data-last (in pipe)
pipe(
  planned,
  symmetricDiff(actual, (a, b) => a.id === b.id)
)

See also diff, union.

union

Merges two collections, removing duplicates using a custom comparator.

Similar to Set union operation but works with objects and custom equality.

Data-first
function union<T>(items1: readonly T[], items2: readonly T[], isEqual?: Comparator<T> | undefined): T[]
Data-last
function union<T>(items2: readonly T[], isEqual?: Comparator<T> | undefined): (items1: readonly T[]) => T[]
Example
// Data-first - with primitive values
union([1, 2, 3], [3, 4, 5])
// => [1, 2, 3, 4, 5]

// With objects and custom comparator
const users1 = [{ id: 1, name: 'Alice' }]
const users2 = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]

union(users1, users2, (a, b) => a.id === b.id)
// => [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]

// Data-last (in pipe)
pipe(
  users1,
  union(users2, (a, b) => a.id === b.id)
)

See also intersect, symmetricDiff.

updateAt

Updates (replaces) an element at a specific index (immutably).

Creates a new array with the element at the specified index replaced with a new value. More ergonomic than splice for single-element replacement. Built on top of Remeda's splice for consistency.

Data-first
function updateAt<T>(items: readonly T[], index: number, value: T): readonly T[]
Data-last
function updateAt<T>(index: number, value: T): (items: readonly T[]) => readonly T[]
Example
// Data-first: Update element
updateAt(['a', 'b', 'c', 'd'], 2, 'X')
// => ['a', 'b', 'X', 'd']

// Update at beginning
updateAt([1, 2, 3], 0, 10)
// => [10, 2, 3]

// Update at end
updateAt([1, 2, 3], 2, 30)
// => [1, 2, 30]

// Negative index (from end)
updateAt(['a', 'b', 'c', 'd'], -1, 'Z')
// => ['a', 'b', 'c', 'Z']

// Out of bounds returns original array
updateAt([1, 2, 3], 10, 99)
// => [1, 2, 3]

// Update todo item
interface Todo {
  id: number
  text: string
  done: boolean
}

const todos: Todo[] = [
  { id: 1, text: 'Buy milk', done: false },
  { id: 2, text: 'Read book', done: false },
  { id: 3, text: 'Walk dog', done: false },
]

updateAt(todos, 1, { id: 2, text: 'Read book', done: true })
// => [
//   { id: 1, text: 'Buy milk', done: false },
//   { id: 2, text: 'Read book', done: true },
//   { id: 3, text: 'Walk dog', done: false },
// ]

// Data-last (in pipe)
pipe(
  ['a', 'b', 'c', 'd'],
  updateAt(2, 'X')
)
// => ['a', 'b', 'X', 'd']

See also insertAt, removeAtIndex, moveIndex.

windowSliding

Creates a sliding window over an array with configurable size and step.

A sliding window creates overlapping sub-arrays by moving a fixed-size window across the array. Useful for moving averages, n-gram analysis, pattern detection, and time-series analysis.

Data-first
function windowSliding<T>(items: readonly T[], config: WindowSlidingConfig): readonly (readonly T[])[]
Data-last
function windowSliding<T>(config: WindowSlidingConfig): (items: readonly T[]) => readonly (readonly T[])[]
Example
// Data-first: Simple sliding window (step=1)
windowSliding([1, 2, 3, 4, 5], { size: 3 })
// => [[1, 2, 3], [2, 3, 4], [3, 4, 5]]

// Sliding window with step
windowSliding([1, 2, 3, 4, 5, 6], { size: 3, step: 2 })
// => [[1, 2, 3], [3, 4, 5]]

// Moving average calculation
const prices = [100, 102, 101, 105, 103, 107]
pipe(
  prices,
  windowSliding({ size: 3 }),
  R.map(window => window.reduce((a, b) => a + b, 0) / window.length)
)
// => [101, 102.66666666666667, 103, 105]

// N-gram analysis
const words = ['the', 'quick', 'brown', 'fox', 'jumps']
windowSliding(words, { size: 2 })
// => [['the', 'quick'], ['quick', 'brown'], ['brown', 'fox'], ['fox', 'jumps']]

// Pattern detection (3-item sequences)
const sequence = [1, 2, 3, 2, 3, 4, 3, 4, 5]
pipe(
  sequence,
  windowSliding({ size: 3 }),
  R.filter(window => window[0]! < window[1]! && window[1]! < window[2]!)
)
// => [[1, 2, 3], [2, 3, 4], [3, 4, 5]]

// Data-last (in pipe)
pipe(
  [1, 2, 3, 4, 5],
  windowSliding({ size: 3 })
)
// => [[1, 2, 3], [2, 3, 4], [3, 4, 5]]

See also chunk, batchBy.

Values

DuplicateKeyError

Error thrown when a duplicate key is found during indexing.

Signature
const DuplicateKeyError: typeof DuplicateKeyError

Types

Comparator

Comparator function for custom equality checks.

Signature
type Comparator<T> = (a: T, b: T) => boolean

CursorPaginatedResult

Result of a cursor-based pagination operation.

Signature
interface CursorPaginatedResult<T, TCursor> {
  readonly items: readonly T[]
  readonly nextCursor?: TCursor | undefined
  readonly hasMore: boolean
}
Example
const page: CursorPaginatedResult<User, string> = {
  items: users,
  nextCursor: 'user_456',
  hasMore: true
}

CursorPaginationConfig

Cursor-based pagination configuration.

Signature
interface CursorPaginationConfig<TCursor> {
  readonly cursor?: TCursor
  readonly limit: number
}
Example
const config: CursorPaginationConfig<string> = {
  cursor: 'user_123',
  limit: 20
}

DiffResult

Result of a diff operation showing added, updated, and removed items.

Signature
interface DiffResult<T> {
  readonly added: readonly T[]
  readonly updated: readonly UpdatedItem<T>[]
  readonly removed: readonly T[]
  readonly unchanged: readonly T[]
}
Example
const result: DiffResult<User> = {
  added: [newUser],
  updated: [{ old: oldUser, new: updatedUser }],
  removed: [deletedUser],
  unchanged: [sameUser]
}

FlattenConfig

Configuration for flatten operation.

Signature
interface FlattenConfig<T> {
  readonly getChildren: (node: T) => readonly T[] | undefined
  readonly maxDepth?: number
  readonly includePath?: boolean
}
Example
const config: FlattenConfig<FileNode> = {
  getChildren: (node) => node.children,
  maxDepth: 3,
  includePath: true
}

FlattenedItem

Result item from flatten operation with path information.

Signature
interface FlattenedItem<T> {
  readonly item: T
  readonly path: readonly T[]
  readonly depth: number
}

IndexConfig

Configuration for index operations with collision handling.

Signature
interface IndexConfig {
  readonly onCollision?: 'error' | 'first' | 'last'
}
Example
const config: IndexConfig = {
  onCollision: 'error' // or 'first' | 'last'
}

NestConfig

Configuration for nested grouping operations.

Signature
interface NestConfig<T> {
  readonly by: readonly (keyof T | ((item: T) => string | number))[]
  readonly createEmpty?: () => unknown
}
Example
const config: NestConfig<Comment> = {
  by: ['userId', 'postId'],
  createEmpty: () => []
}

NestedMap

A nested map structure for hierarchical grouping.

Signature
type NestedMap<T> = {
  [key: string | number]: T | NestedMap<T> | readonly T[]
}
Example
const nested: NestedMap<Comment> = {
  'user_1': {
    'post_1': [comment1, comment2],
    'post_2': [comment3]
  }
}

PaginatedResult

Result of a pagination operation.

Signature
interface PaginatedResult<T> {
  readonly items: readonly T[]
  readonly page: number
  readonly pageSize: number
  readonly total: number
  readonly hasNext: boolean
  readonly hasPrevious: boolean
}
Example
const page: PaginatedResult<User> = {
  items: users,
  page: 1,
  pageSize: 20,
  total: 100,
  hasNext: true,
  hasPrevious: false
}

PaginationConfig

Configuration for pagination operations.

Signature
interface PaginationConfig {
  readonly page: number
  readonly pageSize: number
}
Example
const config: PaginationConfig = {
  page: 1,
  pageSize: 20
}

UpdatedItem

Represents an item that was updated in a diff operation.

Signature
interface UpdatedItem<T> {
  readonly old: T
  readonly new: T
}

WindowSlidingConfig

Configuration for sliding window operation.

Signature
interface WindowSlidingConfig {
  readonly size: number
  readonly step?: number
}
Example
const config: WindowSlidingConfig = {
  size: 3,
  step: 1
}

On this page