← Back to utilities
function

memoize

Creates a memoized version of a function that caches results. Caches function results based on arguments using a simple key-based cache. Improves performance for expensive pure functions by avoiding redundant calculations. Uses JSON.stringify for key generation.

Installation

npx fragmen add function/memoize

Source Code

/**
 * Creates a memoized version of a function that caches results.
 *
 * Caches function results based on arguments using a simple key-based
 * cache. Improves performance for expensive pure functions by avoiding
 * redundant calculations. Uses JSON.stringify for key generation.
 *
 * @tags performance, pure
 * @param {(...args: Args) => R} fn The function to memoize (should be pure)
 * @param {number} maxCacheSize Optional maximum cache size (defaults to unlimited)
 * @returns {((...args: Args) => R) & { clear: () => void }} Memoized version of the function with a clear() method
 *
 * @example
 * ```typescript
 * // Expensive calculation
 * const fibonacci = (n: number): number => {
 *   if (n <= 1) return n;
 *   return fibonacci(n - 1) + fibonacci(n - 2);
 * };
 *
 * const memoizedFib = memoize(fibonacci);
 * console.log(memoizedFib(40)); // First call: slow
 * console.log(memoizedFib(40)); // Cached: instant
 *
 * // Multiple arguments
 * const add = (a: number, b: number) => a + b;
 * const memoizedAdd = memoize(add);
 * memoizedAdd(1, 2); // Calculates and caches
 * memoizedAdd(1, 2); // Returns cached result
 *
 * // Limited cache size
 * const memoizedWithLimit = memoize(expensiveFn, 100);
 *
 * // Clear cache manually
 * memoizedAdd.clear();
 * ```
 */
export function memoize<TArgs extends unknown[], TResult>(
  fn: (...args: TArgs) => TResult,
  maxCacheSize?: number
): ((...args: TArgs) => TResult) & { clear: () => void } {
  const cache = new Map<string, TResult>();

  const memoized = (...args: TArgs): TResult => {
    // Custom key generation that distinguishes null from undefined
    const key = JSON.stringify(args, (_, value) => {
      if (value === undefined) {
        return '__undefined__';
      }
      return value;
    });

    if (cache.has(key)) {
      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
      return cache.get(key)!;
    }

    const result = fn(...args);
    cache.set(key, result);

    // Enforce max cache size (FIFO eviction)
    if (maxCacheSize && cache.size > maxCacheSize) {
      const firstKey = cache.keys().next().value as string;
      if (firstKey) {
        cache.delete(firstKey);
      }
    }

    return result;
  };

  memoized.clear = () => {
    cache.clear();
  };

  return memoized;
}

Examples

// Expensive calculation
const fibonacci = (n: number): number => {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
};

const memoizedFib = memoize(fibonacci);
console.log(memoizedFib(40)); // First call: slow
console.log(memoizedFib(40)); // Cached: instant

// Multiple arguments
const add = (a: number, b: number) => a + b;
const memoizedAdd = memoize(add);
memoizedAdd(1, 2); // Calculates and caches
memoizedAdd(1, 2); // Returns cached result

// Limited cache size
const memoizedWithLimit = memoize(expensiveFn, 100);

// Clear cache manually
memoizedAdd.clear();

Related Utilities

debounce

function

Creates a debounced function that delays invoking the provided function until after wait milliseconds have elapsed. Prevents rapid successive calls by canceling previous timeouts. Useful for handling events like search input, button clicks, or window resize where you want to limit the frequency of function execution.

#performance#event-handling

once

function

Creates a function that can only be invoked once. Subsequent calls return the result of the first invocation. Ensures a function is executed exactly one time, regardless of how many times it's called. Useful for initialization functions, event handlers that should only fire once, or expensive operations that should only run once.

#performance

throttle

function

Creates a throttled function that only invokes the provided function at most once per specified time period. Unlike debounce which delays execution, throttle ensures the function is called at regular intervals. The first call is executed immediately, and subsequent calls within the wait period are ignored. Useful for rate-limiting events like scroll, mousemove, or API requests.

#performance

chunk

array

Splits an array into chunks of a specified size. Creates a new array containing subarrays (chunks) of the original array, each with a maximum length of the specified size. The last chunk may contain fewer elements if the array length is not evenly divisible by the chunk size.

#pure#array-manipulation

compact

array

Removes falsy values from an array. Creates a new array with all falsy values removed. Falsy values are: false, null, 0, "", undefined, and NaN. This is useful for cleaning arrays and ensuring only truthy values remain.

#pure#array-manipulation

difference

array

Creates an array of values from the first array that are not present in the other arrays. Returns a new array containing elements that exist in the first array but not in any of the subsequent arrays. The order of elements follows the order of the first array. Duplicates in the first array are preserved unless they appear in other arrays.

#pure#array-manipulation

Quick Actions

Estimated size:2.25 KB

Tags

Parameters

fn(...args: Args) => R

The function to memoize (should be pure)

maxCacheSizenumber

Optional maximum cache size (defaults to unlimited)

Returns

unknown

{((...args: Args) => R) & { clear: () => void }} Memoized version of the function with a clear() method