-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutil.js
68 lines (59 loc) · 1.79 KB
/
util.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
const last = xs => xs[xs.length - 1];
export const range = n =>
Array(n)
.fill()
.map((_, i) => i);
const pairs = xs => xs.slice(1).map((_, i) => [xs[i], xs[i + 1]]);
export const sample = xs => xs[Math.floor(Math.random() * xs.length)];
/**
* default value weight is 1, override weight in `weightMap`. Weight `2` value
* in `weightMap` is 2x more likely than 1, etc.
*/
export const randomWeighted = (xs, weightMap = {}) => {
const probabilities = xs.map(value => ({
value,
weight: weightMap[value] || 1
}));
const totalWeight = probabilities.reduce(
(totalWeight, { weight }) => totalWeight + weight,
0
);
const normalizedProbabilities = probabilities
.map(({ weight }) => weight / totalWeight)
.reduce((normPs, np) => normPs.concat((last(normPs) || 0) + np), []);
const roll = Math.random();
for (let index in normalizedProbabilities) {
if (roll < normalizedProbabilities[index])
return {
...probabilities[index],
percentage:
normalizedProbabilities[index] -
(normalizedProbabilities[index - 1] || 0)
};
}
throw new Error("not implemented correctly");
};
export const randomBetween = ([low, high]) =>
Math.floor((high - low) * Math.random() + low);
/**
* @returns number [1, 100] weighted towards 1
*/
export const logRandomMax100 = () => {
const maxN = 100;
const n = 1.1;
const t = n ** maxN;
return Math.floor(1 + maxN - Math.log(1 + Math.random() * t) / Math.log(n));
};
// FOR TESTING, mostly
const bucket = (xs, idFn) =>
xs.reduce(
(acc, x) => ({
...acc,
[idFn(x)]: (acc[idFn(x)] || []).concat(x)
}),
{}
);
const counts = (xs, idFn = id => id) =>
Object.values(bucket(xs, idFn))
.map(x => [idFn(x[0]), x.length])
.sort(([, lhs], [, rhs]) => rhs - lhs);