pick-me-too 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -1
- package/lib/index.js +37 -17
- package/package.json +1 -1
- package/src/__tests__/documentation.test.ts +0 -1
- package/src/index.ts +37 -16
package/CHANGELOG.md
CHANGED
package/lib/index.js
CHANGED
|
@@ -74,7 +74,10 @@ exports.pickMe = pickMe;
|
|
|
74
74
|
* @returns {(rng: Rng) => () => T} - a function from random number generators to pickers of things in the frequency list
|
|
75
75
|
*/
|
|
76
76
|
function pickMeToo(frequencies) {
|
|
77
|
-
|
|
77
|
+
const simpleton = simplePicker(frequencies);
|
|
78
|
+
if (simpleton)
|
|
79
|
+
return simpleton;
|
|
80
|
+
frequencies = sanitize(frequencies);
|
|
78
81
|
// short-circuit edge cases
|
|
79
82
|
if (frequencies.length === 0)
|
|
80
83
|
throw new Error(`nothing to select in ${frequencies}`);
|
|
@@ -82,15 +85,7 @@ function pickMeToo(frequencies) {
|
|
|
82
85
|
const [t] = frequencies[0];
|
|
83
86
|
return (_rng) => () => t;
|
|
84
87
|
}
|
|
85
|
-
|
|
86
|
-
// it is difficult to do this efficiently because we allow T to be anything
|
|
87
|
-
for (let i = 0, lim = frequencies.length - 1; i < lim; i++) {
|
|
88
|
-
const t = frequencies[i][0];
|
|
89
|
-
for (let j = i + 1; j < frequencies.length; j++) {
|
|
90
|
-
if (t === frequencies[j][0])
|
|
91
|
-
throw new Error(`${t} appears more than once among frequencies`);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
88
|
+
dupChecker(frequencies, (item) => item[0]);
|
|
94
89
|
let total = 0, acc = 0;
|
|
95
90
|
for (const [, n] of frequencies)
|
|
96
91
|
total += n;
|
|
@@ -108,14 +103,39 @@ function pickMeToo(frequencies) {
|
|
|
108
103
|
return (rng) => () => items[f(rng())];
|
|
109
104
|
}
|
|
110
105
|
exports.pickMeToo = pickMeToo;
|
|
111
|
-
//
|
|
112
|
-
|
|
106
|
+
// throw an error in case of duplicates
|
|
107
|
+
// it is difficult to do this efficiently because we allow T to be anything, so we can't for instance use a Set
|
|
108
|
+
function dupChecker(frequencies, toT) {
|
|
109
|
+
for (let i = 0, lim = frequencies.length - 1; i < lim; i++) {
|
|
110
|
+
const t = toT(frequencies[i]);
|
|
111
|
+
for (let j = i + 1; j < frequencies.length; j++) {
|
|
112
|
+
if (t === toT(frequencies[j]))
|
|
113
|
+
throw new Error(`${t} appears more than once among frequencies`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// if all frequencies are the same, we can make a simpler picker
|
|
118
|
+
function simplePicker(frequencies) {
|
|
113
119
|
if (frequencies.length === 0)
|
|
114
|
-
return
|
|
115
|
-
if (frequencies.every((s) => Array.isArray(s) && s.length === 2 && typeof s[1] === 'number'))
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
120
|
+
return;
|
|
121
|
+
if (frequencies.every((s) => Array.isArray(s) && s.length === 2 && typeof s[1] === 'number')) {
|
|
122
|
+
const castFrequencies = frequencies;
|
|
123
|
+
const [, n1] = castFrequencies[0];
|
|
124
|
+
if (n1 > 0 && castFrequencies.every(([, n2]) => n2 === n1)) {
|
|
125
|
+
const items = castFrequencies.map(([t]) => t);
|
|
126
|
+
dupChecker(items, (item) => item);
|
|
127
|
+
return (rng) => () => items[Math.floor(items.length * rng())];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
const items = [...frequencies];
|
|
132
|
+
if (items.length === 1) {
|
|
133
|
+
const t = items[0];
|
|
134
|
+
return (_rng) => () => t;
|
|
135
|
+
}
|
|
136
|
+
dupChecker(items, (item) => item);
|
|
137
|
+
return (rng) => () => items[Math.floor(items.length * rng())];
|
|
138
|
+
}
|
|
119
139
|
}
|
|
120
140
|
// remove impossible frequencies
|
|
121
141
|
function sanitize(frequencies) {
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -71,21 +71,18 @@ export function pickMe<T>(frequencies: [T, number][] | T[], rando?: Rng): () =>
|
|
|
71
71
|
* @returns {(rng: Rng) => () => T} - a function from random number generators to pickers of things in the frequency list
|
|
72
72
|
*/
|
|
73
73
|
export function pickMeToo<T>(frequencies: [T, number][] | T[]): (rng: Rng) => () => T {
|
|
74
|
-
|
|
74
|
+
const simpleton = simplePicker(frequencies)
|
|
75
|
+
if (simpleton) return simpleton
|
|
76
|
+
|
|
77
|
+
frequencies = sanitize(frequencies as any as [T, number][])
|
|
78
|
+
|
|
75
79
|
// short-circuit edge cases
|
|
76
80
|
if (frequencies.length === 0) throw new Error(`nothing to select in ${frequencies}`)
|
|
77
81
|
if (frequencies.length === 1) {
|
|
78
82
|
const [t] = frequencies[0]
|
|
79
83
|
return (_rng: Rng) => () => t
|
|
80
84
|
}
|
|
81
|
-
|
|
82
|
-
// it is difficult to do this efficiently because we allow T to be anything
|
|
83
|
-
for (let i = 0, lim = frequencies.length - 1; i < lim; i++) {
|
|
84
|
-
const t = frequencies[i][0]
|
|
85
|
-
for (let j = i + 1; j < frequencies.length; j++) {
|
|
86
|
-
if (t === frequencies[j][0]) throw new Error(`${t} appears more than once among frequencies`)
|
|
87
|
-
}
|
|
88
|
-
}
|
|
85
|
+
dupChecker(frequencies, (item: [T, number] | T) => (item as [T, number])[0])
|
|
89
86
|
let total = 0,
|
|
90
87
|
acc = 0
|
|
91
88
|
for (const [, n] of frequencies) total += n
|
|
@@ -111,13 +108,37 @@ export function pickMeToo<T>(frequencies: [T, number][] | T[]): (rng: Rng) => ()
|
|
|
111
108
|
*/
|
|
112
109
|
export type Rng = () => number
|
|
113
110
|
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
111
|
+
// throw an error in case of duplicates
|
|
112
|
+
// it is difficult to do this efficiently because we allow T to be anything, so we can't for instance use a Set
|
|
113
|
+
function dupChecker<T>(frequencies: [T, number][] | T[], toT: (item: [T, number] | T) => T) {
|
|
114
|
+
for (let i = 0, lim = frequencies.length - 1; i < lim; i++) {
|
|
115
|
+
const t = toT(frequencies[i])
|
|
116
|
+
for (let j = i + 1; j < frequencies.length; j++) {
|
|
117
|
+
if (t === toT(frequencies[j])) throw new Error(`${t} appears more than once among frequencies`)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// if all frequencies are the same, we can make a simpler picker
|
|
123
|
+
function simplePicker<T>(frequencies: [T, number][] | T[]): undefined | ((rng: Rng) => () => T) {
|
|
124
|
+
if (frequencies.length === 0) return
|
|
125
|
+
if ((frequencies as any).every((s: any) => Array.isArray(s) && s.length === 2 && typeof s[1] === 'number')) {
|
|
126
|
+
const castFrequencies: [T, number][] = frequencies as any as [T, number][]
|
|
127
|
+
const [, n1] = castFrequencies[0]
|
|
128
|
+
if (n1 > 0 && castFrequencies.every(([, n2]) => n2 === n1)) {
|
|
129
|
+
const items = castFrequencies.map(([t]) => t)
|
|
130
|
+
dupChecker(items, (item: [T, number] | T) => item as T)
|
|
131
|
+
return (rng: Rng) => () => items[Math.floor(items.length * rng())]
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
const items: T[] = [...frequencies] as any as T[]
|
|
135
|
+
if (items.length === 1) {
|
|
136
|
+
const t = items[0]
|
|
137
|
+
return (_rng: Rng) => () => t
|
|
138
|
+
}
|
|
139
|
+
dupChecker(items, (item: [T, number] | T) => item as T)
|
|
140
|
+
return (rng: Rng) => () => items[Math.floor(items.length * rng())]
|
|
141
|
+
}
|
|
121
142
|
}
|
|
122
143
|
|
|
123
144
|
// remove impossible frequencies
|