Created
January 16, 2021 15:43
-
-
Save jaredpalmer/a77da0b1d8b15f370759e2eecae5686d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { crc32 } from 'common/components/Features/Experiment/crc32'; | |
import { Config } from 'common/api/models/Config'; | |
const sortBy = require('lodash.sortby'); | |
export interface Experiments { | |
[name: string]: Variant[]; | |
} | |
export interface Variant { | |
name: string; | |
weight: string; | |
} | |
/** Returns selected variants of given experiments */ | |
export function getVariants( | |
{ config: { experiments = {} } }: Config, | |
userId: string | |
) { | |
return Object.keys(experiments).reduce( | |
(acc, name) => ({ | |
...acc, | |
[name]: assignExperimentVariant(experiments[name], userId), | |
}), | |
{} | |
); | |
} | |
/** Select a variant of an experiment (deterministic if given userId) */ | |
export function assignExperimentVariant(variants: Variant[], userId: string) { | |
/* | |
Choosing a weighted variant: | |
For C, A, B with weights 2, 4, 8 | |
variants = A, B, C | |
weights = 4, 8, 2 | |
weightSum = 14 | |
weightedIndex = 9 | |
AAAABBBBBBBBCC | |
========^ | |
Select B | |
*/ | |
const sortedVariants = sortBy(variants, ({ name }: Variant) => name); | |
const weights = sortedVariants.map(({ weight }: Variant) => | |
parseInt(weight, 10) | |
); | |
const weightSum = weights.reduce((acc: number, weight: number) => { | |
return acc + weight; | |
}, 0); | |
// A random number between 0 and weightSum | |
let weightedIndex = Math.abs(crc32(userId) % weightSum); | |
// Iterate through the sorted weights, and deduct each from the weightedIndex. | |
// If weightedIndex drops < 0, select the variant. If weightedIndex does not | |
// drop < 0, default to the last variant in the array that is initially assigned. | |
let selectedVariant = sortedVariants[sortedVariants.length - 1].name; | |
for (let index = 0; index < weights.length; index++) { | |
weightedIndex -= weights[index]; | |
if (weightedIndex < 0) { | |
selectedVariant = sortedVariants[index].name; | |
break; | |
} | |
} | |
return selectedVariant; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment