( array: ArrayLike<T>, options?: SampleOptions, )
| 54 | * ``` |
| 55 | */ |
| 56 | export function sample<T>( |
| 57 | array: ArrayLike<T>, |
| 58 | options?: SampleOptions, |
| 59 | ): T | undefined { |
| 60 | const { weights } = { ...options }; |
| 61 | |
| 62 | if (weights) { |
| 63 | if (weights.length !== array.length) { |
| 64 | throw new RangeError( |
| 65 | "Cannot sample an item: The length of the weights array must match the length of the input array", |
| 66 | ); |
| 67 | } |
| 68 | |
| 69 | if (!array.length) return undefined; |
| 70 | |
| 71 | const total = Object.values(weights).reduce((sum, n) => sum + n, 0); |
| 72 | |
| 73 | if (total <= 0) { |
| 74 | throw new RangeError( |
| 75 | "Cannot sample an item: Total weight must be greater than 0", |
| 76 | ); |
| 77 | } |
| 78 | |
| 79 | const rand = (options?.prng ?? Math.random)() * total; |
| 80 | let current = 0; |
| 81 | |
| 82 | for (let i = 0; i < array.length; ++i) { |
| 83 | current += weights[i]!; |
| 84 | if (rand < current) { |
| 85 | return array[i]!; |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | // this line should never be hit, but in case of rounding errors etc. |
| 90 | return array[0]!; |
| 91 | } |
| 92 | |
| 93 | const length = array.length; |
| 94 | return length |
| 95 | ? array[randomIntegerBetween(0, length - 1, options)] |
| 96 | : undefined; |
| 97 | } |
no test coverage detected