Skip to Content
Delmare Digital

Assignment

GrowthBook's SDK hash and bucket ranges, and the helpers payload-experiments uses for weights, layers and holdouts.

Assignment functions turn a seed and a unit id into a group, deterministically, so any process computes the same answer without storing it.

SDK functions

Ported from GrowthBook's packages/sdk-js/src/util.ts.

// SDK hash; unsupported versions return null. Plugin assignment uses version 2.
hash(seed: string, value: string, version: number): number | null
// Equal fractions for n variations.
getEqualWeights(n: number): number[]
// Test membership in a half-open [start, end) range.
inRange(n: number, range: VariationRange): boolean
// SDK ranges for fractional weights and coverage (undefined means 1).
getBucketRanges(numVariations: number, coverage: number | undefined, weights?: number[]): VariationRange[]
// First matching range index, or -1 when not enrolled.
chooseVariation(n: number, ranges: VariationRange[]): number

VariationRange is [number, number].

Plugin helpers

// Validate non-negative integer weights summing to 100, then divide by 100.
armWeightsToFractions(weights: readonly number[]): number[]
// Compose SDK hash v2 and ranges; seed must end in '|'.
assignArm(args: { seed: string; unitId: string; weights: readonly number[]; coverage: number }): number
// Hash into one of 100 layer slices, numbered 0–99.
layerSlice(layerSlug: string, unitId: string): number
// Reuse the test phase's last range for holdout and construct the winner range.
rolloutBucketRanges(testWeights: readonly number[], coverage: number): { winner: VariationRange; holdout: VariationRange }
import { assignArm } from '@delmaredigital/experiments-stats'

const arm = assignArm({
  seed: 'checkout|',
  unitId: 'opaque-unit-id',
  weights: [45, 45, 10],
  coverage: 0.5,
})
// arm is an index into weights; -1 means outside allocation.
  • Weights are whole numbers that total 100. A holdout is an explicit last arm.
  • Coverage is a separate allocation fraction. Raising coverage widens each range without moving an already enrolled unit to another arm.
  • rolloutBucketRanges requires at least two arms and a nonzero holdout. It reuses the test holdout range exactly, including floating-point endpoints; the winner range is [0, coverage * holdout[0]). Pass the test phase's weights and coverage, and always use this function rather than recomputing a holdout from its percentage: holdout membership depends on the original range's exact endpoints.
Edit this page on GitHub

On this page