Skip to content

Instantly share code, notes, and snippets.

@jaredpalmer
Created January 16, 2021 15:43
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaredpalmer/a77da0b1d8b15f370759e2eecae5686d to your computer and use it in GitHub Desktop.
Save jaredpalmer/a77da0b1d8b15f370759e2eecae5686d to your computer and use it in GitHub Desktop.
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