trekhleb/javascript-algorithms · error · Error

Items and weights must be of the same size

Error message

Items and weights must be of the same size

What it means

weightedRandom(items, weights) requires exactly one weight per item; the first guard rejects any length mismatch. Weights are accumulated into cumulative sums and a random number in [0, sum] selects the item, so a missing or extra weight leaves the item-to-probability mapping undefined. The size check runs before the emptiness check, so mismatched non-empty arrays always produce this message rather than 'Items must not be empty'.

Source

Thrown at src/algorithms/statistics/weighted-random/weightedRandom.js:18

/**
 * Picks the random item based on its weight.
 * The items with higher weight will be picked more often (with a higher probability).
 *
 * For example:
 * - items = ['banana', 'orange', 'apple']
 * - weights = [0, 0.2, 0.8]
 * - weightedRandom(items, weights) in 80% of cases will return 'apple', in 20% of cases will return
 * 'orange' and it will never return 'banana' (because probability of picking the banana is 0%)
 *
 * @param {any[]} items
 * @param {number[]} weights
 * @returns {{item: any, index: number}}
 */
/* eslint-disable consistent-return */
export default function weightedRandom(items, weights) {
  if (items.length !== weights.length) {
    throw new Error('Items and weights must be of the same size');
  }

  if (!items.length) {
    throw new Error('Items must not be empty');
  }

  // Preparing the cumulative weights array.
  // For example:
  // - weights = [1, 4, 3]
  // - cumulativeWeights = [1, 5, 8]
  const cumulativeWeights = [];
  for (let i = 0; i < weights.length; i += 1) {
    cumulativeWeights[i] = weights[i] + (cumulativeWeights[i - 1] || 0);
  }

  // Getting the random number in a range of [0...sum(weights)]
  // For example:
  // - weights = [1, 4, 3]

View on GitHub (pinned to 85293e3e2b)

Solutions

  1. Log items.length and weights.length to find the drift
  2. Rebuild both arrays from one source so they cannot diverge, e.g. derive weights with items.map(...)
  3. When filtering items, filter index-aligned pairs and unzip afterwards
  4. For uniform weights pass items.map(() => 1) instead of a hand-maintained array

Example fix

// before
import weightedRandom from './src/algorithms/statistics/weighted-random/weightedRandom';
const pick = weightedRandom(['a', 'b', 'c'], [1, 2]);
// throws: 3 items vs 2 weights

// after
const items = ['a', 'b', 'c'];
const weights = items.map((item) => (item === 'a' ? 1 : 2));
const pick = weightedRandom(items, weights);
// lengths always match
Defensive patterns

Strategy: validation

Validate before calling

if (!Array.isArray(items) || !Array.isArray(weights) || items.length !== weights.length) {
  throw new TypeError('items and weights must be arrays of equal length');
}
weightedRandom(items, weights);

Type guard

const isWeightedPair = (items, weights) =>
  Array.isArray(items) && Array.isArray(weights)
  && items.length === weights.length && items.length > 0;

Try / catch

try {
  pick = weightedRandom(items, weights);
} catch (e) {
  if (e.message === 'Items and weights must be of the same size') {
    pick = { item: items[0], index: 0 }; // degrade to first item
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: weightedRandom(['a','b','c'], [1, 2]) (3 items, 2 weights); items extended without extending weights; weights built from a different data source or filtered separately from items.

Common situations: Weights read from config while items come from a database, items filtered (e.g. out-of-stock removed) without refiltering weights, or parallel arrays drifting apart across refactors.

Related errors


AI-assisted analysis of trekhleb/javascript-algorithms@85293e3e2b (2026-08-24). Data as JSON: /api/errors/b5388bdaad5dbed8. Report an issue: GitHub.