Skip to content
VersionSize

reduce

The reduce utility reduces an array to a single value by executing a reducer function on each element, passing in the return value from the calculation on the preceding element.

Implementation

View Source Code
ts
import { assert } from '../function/assert';
import { IS_ARRAY_ERROR_MSG, isArray } from '../typed/isArray';
import { isPromise } from '../typed/isPromise';
import type { Result } from '../types';

type SyncCallback<R, T> = (prev: R, curr: T, index: number, array: T[]) => R;
type AsyncCallback<R, T> = (prev: R, curr: T, index: number, array: T[]) => Promise<R>;
type Callback<R, T> = SyncCallback<R, T> | AsyncCallback<R, T>;

/**
 * Reduces an array to a single value by applying a callback function
 * to each element in the array, passing the accumulated result and
 * the current element as arguments. Supports both synchronous and
 * asynchronous callback functions.
 *
 * @example
 * ```ts
 * const arr = [1, 2, 3];
 * reduce(arr, (acc, curr) => acc + curr, 0); // 10
 * await reduce(arr, async (acc, curr) => acc + curr, 0); // 10
 * ```
 *
 * @param array - The array to reduce.
 * @param callback - The callback function to apply to each element.
 * @param initialValue - The initial value for the accumulator.
 *
 * @returns The reduced value, or a Promise that resolves to the reduced value if the callback is asynchronous.
 *
 * @throws {TypeError} If the first argument is not an array.
 */
export function reduce<T, R, C extends Callback<R, T>>(array: T[], callback: C, initialValue: R) {
  assert(isArray(array), IS_ARRAY_ERROR_MSG, { args: { array }, type: TypeError });

  const lazy = isPromise(callback);

  let acc: R | Promise<R> = lazy ? Promise.resolve(initialValue) : initialValue;

  for (let i = 0; i < array.length; i++) {
    if (lazy) {
      acc = (acc as Promise<R>).then((resolvedAcc) => callback(resolvedAcc, array[i], i, array));
    } else {
      acc = callback(acc as R, array[i], i, array);
    }
  }

  return acc as Result<C>;
}

reduce.fn = true;

Features

  • Isomorphic: Works in both Browser and Node.js.
  • Type-safe: Correctly handles types for both elements and the accumulator.
  • Async Support: If the callback returns a Promise, reduce will return a Promise that resolves to the final value once all elements are processed sequentially.

API

ts
function reduce<T, R>(
  array: T[],
  callback: (acc: R, item: T, index: number, array: T[]) => R | Promise<R>,
  initialValue: R,
): R | Promise<R>;

Parameters

  • array: The array to reduce.
  • callback: The reducer function called for every element. It receives:
    • acc: The accumulated value.
    • item: The current element.
    • index: The index of the current element.
    • array: The original array.
  • initialValue: The value to use as the first argument to the first call of the callback.

Returns

  • The accumulated result.
  • A Promise<R> if the callback is asynchronous.

Examples

Basic Aggregation

ts
import { reduce } from '@vielzeug/toolkit';

const numbers = [1, 2, 3, 4];
const sum = reduce(numbers, (acc, x) => acc + x, 0); // 10

Building Objects

ts
import { reduce } from '@vielzeug/toolkit';

const pairs: [string, number][] = [
  ['a', 1],
  ['b', 2],
  ['c', 3],
];
const obj = reduce(
  pairs,
  (acc, [key, val]) => {
    acc[key] = val;
    return acc;
  },
  {} as Record<string, any>,
);
// { a: 1, b: 2, c: 3 }

Asynchronous Reduction

ts
import { reduce, delay } from '@vielzeug/toolkit';

const tasks = [1, 2, 3];
const result = await reduce(
  tasks,
  async (acc, task) => {
    await delay(100); // Process task sequentially
    return acc + task;
  },
  0,
); // 6

Implementation Notes

  • Throws TypeError if the first argument is not an array.
  • Sequential Execution: Unlike map and filter, asynchronous reduce processes elements one after another, as the next call depends on the previous result.

See Also

  • map: Transform each element in an array.
  • filter: Subset an array.
  • aggregate: For more complex aggregation patterns.