alea-deck 4.1.4 → 5.0.1
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/LICENSE.txt +1 -1
- package/README.md +21 -9
- package/dist/index.cjs +100 -0
- package/dist/index.d.cts +40 -0
- package/dist/index.d.mts +40 -0
- package/dist/index.mjs +73 -0
- package/package.json +45 -30
- package/index.js +0 -111
package/LICENSE.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copyright 2014–
|
|
1
|
+
Copyright 2014–2026 Kenan Yildirim <https://kenany.me/>
|
|
2
2
|
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
4
|
this software and associated documentation files (the "Software"), to deal in
|
package/README.md
CHANGED
|
@@ -7,15 +7,15 @@ Uniform and weighted shuffling and sampling, just like
|
|
|
7
7
|
## Example
|
|
8
8
|
|
|
9
9
|
``` javascript
|
|
10
|
-
|
|
10
|
+
import { shuffle, pick } from 'alea-deck';
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
shuffle([1, 2, 3, 4]);
|
|
13
13
|
// => [1, 4, 2, 3]
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
pick([1, 2, 3, 4]);
|
|
16
16
|
// => 2
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
shuffle({
|
|
19
19
|
a: 10,
|
|
20
20
|
b: 8,
|
|
21
21
|
c: 2,
|
|
@@ -24,7 +24,7 @@ deck.shuffle({
|
|
|
24
24
|
});
|
|
25
25
|
// => ['b', 'a', 'c', 'd', 'e']
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
pick({
|
|
28
28
|
a: 10,
|
|
29
29
|
b: 8,
|
|
30
30
|
c: 2,
|
|
@@ -43,10 +43,22 @@ $ npm install alea-deck
|
|
|
43
43
|
## API
|
|
44
44
|
|
|
45
45
|
``` javascript
|
|
46
|
-
|
|
46
|
+
import { deck, shuffle, pick, normalize } from 'alea-deck';
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
### `deck
|
|
49
|
+
### `deck(collection)`
|
|
50
|
+
|
|
51
|
+
Binds `shuffle` and `pick` to `collection`, returning an object with both
|
|
52
|
+
methods pre-applied.
|
|
53
|
+
|
|
54
|
+
``` javascript
|
|
55
|
+
const d = deck([1, 2, 3, 4]);
|
|
56
|
+
|
|
57
|
+
d.shuffle(); // => [3, 1, 4, 2]
|
|
58
|
+
d.pick(); // => 3
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### `shuffle(collection)`
|
|
50
62
|
|
|
51
63
|
If `collection` is an _Array_, returns a new shuffled _Array_ based on a unifrom
|
|
52
64
|
distributionm without mutating the original _Array_.
|
|
@@ -54,7 +66,7 @@ distributionm without mutating the original _Array_.
|
|
|
54
66
|
Otherwise, if `collection` is an _Object_, returns a new shuffled _Array_ of
|
|
55
67
|
`collection`'s visible keys based on the value weights of `collection`.
|
|
56
68
|
|
|
57
|
-
### `
|
|
69
|
+
### `pick(collection)`
|
|
58
70
|
|
|
59
71
|
Samples `collection` without mutating `collection`.
|
|
60
72
|
|
|
@@ -64,7 +76,7 @@ uniform distribution.
|
|
|
64
76
|
Otherwise, if `collection` is an _Object_, returns a random key from
|
|
65
77
|
`collection` biased by its normalized value.
|
|
66
78
|
|
|
67
|
-
### `
|
|
79
|
+
### `normalize(obj)`
|
|
68
80
|
|
|
69
81
|
Return a new `obj` _Object_ where the values have been divided by the sum of all
|
|
70
82
|
the values such that the sum of all the values in the returned _Object_ is 1.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
//#endregion
|
|
24
|
+
let _thi_ng_checks = require("@thi.ng/checks");
|
|
25
|
+
let alea_random = require("alea-random");
|
|
26
|
+
alea_random = __toESM(alea_random);
|
|
27
|
+
//#region src/index.ts
|
|
28
|
+
function deck(xs) {
|
|
29
|
+
if (!(Array.isArray(xs) || (0, _thi_ng_checks.isPlainObject)(xs))) throw new TypeError("Must be an Array or an Object");
|
|
30
|
+
return {
|
|
31
|
+
shuffle: shuffle.bind(null, xs),
|
|
32
|
+
pick: pick.bind(null, xs)
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function shuffle(xs) {
|
|
36
|
+
if (Array.isArray(xs)) {
|
|
37
|
+
const res = xs.slice();
|
|
38
|
+
for (let i = res.length - 1; i >= 0; i--) {
|
|
39
|
+
const n = Math.floor((0, alea_random.default)(0, 1, true) * i);
|
|
40
|
+
const t = res[i];
|
|
41
|
+
res[i] = res[n];
|
|
42
|
+
res[n] = t;
|
|
43
|
+
}
|
|
44
|
+
return res;
|
|
45
|
+
}
|
|
46
|
+
if ((0, _thi_ng_checks.isPlainObject)(xs)) {
|
|
47
|
+
const weights = { ...xs };
|
|
48
|
+
const ret = [];
|
|
49
|
+
while (Object.keys(weights).length > 0) {
|
|
50
|
+
const key = pick(weights);
|
|
51
|
+
delete weights[key];
|
|
52
|
+
ret.push(key);
|
|
53
|
+
}
|
|
54
|
+
return ret;
|
|
55
|
+
}
|
|
56
|
+
throw new TypeError("Must be an Array or an Object");
|
|
57
|
+
}
|
|
58
|
+
function pick(xs) {
|
|
59
|
+
if (Array.isArray(xs)) {
|
|
60
|
+
if (xs.length === 0) return;
|
|
61
|
+
return xs[Math.floor((0, alea_random.default)(0, 1, true) * xs.length)];
|
|
62
|
+
}
|
|
63
|
+
if ((0, _thi_ng_checks.isPlainObject)(xs)) {
|
|
64
|
+
const weights = normalize(xs);
|
|
65
|
+
if (!weights) return;
|
|
66
|
+
const n = (0, alea_random.default)(0, 1, true);
|
|
67
|
+
let threshold = 0;
|
|
68
|
+
const keyz = Object.keys(weights);
|
|
69
|
+
for (const key of keyz) {
|
|
70
|
+
threshold += weights[key];
|
|
71
|
+
if (n < threshold) return key;
|
|
72
|
+
}
|
|
73
|
+
throw new Error("Exceeded threshold. Something is very wrong.");
|
|
74
|
+
}
|
|
75
|
+
throw new TypeError("Must be an Array or an Object");
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Returns a copy of `weights` where every value has been divided by the total
|
|
79
|
+
* so that all values sum to 1. Returns `undefined` for an empty object.
|
|
80
|
+
*/
|
|
81
|
+
function normalize(weights) {
|
|
82
|
+
if (!(0, _thi_ng_checks.isPlainObject)(weights)) throw new TypeError("`weights` must be an object");
|
|
83
|
+
const keyz = Object.keys(weights);
|
|
84
|
+
if (!keyz.length) return;
|
|
85
|
+
const total = keyz.reduce((sum, key) => {
|
|
86
|
+
const x = weights[key];
|
|
87
|
+
if (x < 0) throw new Error(`Negative weight encountered at key ${key}`);
|
|
88
|
+
if (typeof x !== "number" || Number.isNaN(x)) throw new TypeError(`Number expected, got ${typeof x}`);
|
|
89
|
+
return sum + x;
|
|
90
|
+
}, 0);
|
|
91
|
+
return total === 1 ? weights : keyz.reduce((acc, key) => {
|
|
92
|
+
acc[key] = weights[key] / total;
|
|
93
|
+
return acc;
|
|
94
|
+
}, {});
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
exports.deck = deck;
|
|
98
|
+
exports.normalize = normalize;
|
|
99
|
+
exports.pick = pick;
|
|
100
|
+
exports.shuffle = shuffle;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region src/index.d.ts
|
|
2
|
+
type WeightMap = Record<string, number>;
|
|
3
|
+
/**
|
|
4
|
+
* Binds `shuffle` and `pick` to the given collection, returning an object
|
|
5
|
+
* with both methods pre-applied to `xs`.
|
|
6
|
+
*/
|
|
7
|
+
declare function deck<T>(xs: readonly T[]): {
|
|
8
|
+
shuffle: () => T[];
|
|
9
|
+
pick: () => T | undefined;
|
|
10
|
+
};
|
|
11
|
+
declare function deck(xs: WeightMap): {
|
|
12
|
+
shuffle: () => string[];
|
|
13
|
+
pick: () => string | undefined;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Returns a shuffled copy of `xs` without mutating the original.
|
|
17
|
+
*
|
|
18
|
+
* If `xs` is an Array, elements are reordered with uniform probability.
|
|
19
|
+
* If `xs` is an Object, returns a shuffled array of its keys weighted by
|
|
20
|
+
* their values.
|
|
21
|
+
*/
|
|
22
|
+
declare function shuffle<T>(xs: readonly T[]): T[];
|
|
23
|
+
declare function shuffle(xs: WeightMap): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Samples a single element from `xs` without mutating it.
|
|
26
|
+
*
|
|
27
|
+
* If `xs` is an Array, returns a uniformly random element, or `undefined` if
|
|
28
|
+
* the array is empty.
|
|
29
|
+
* If `xs` is an Object, returns a key sampled with probability proportional to
|
|
30
|
+
* its normalized weight, or `undefined` if the object is empty.
|
|
31
|
+
*/
|
|
32
|
+
declare function pick<T>(xs: readonly T[]): T | undefined;
|
|
33
|
+
declare function pick(xs: WeightMap): string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Returns a copy of `weights` where every value has been divided by the total
|
|
36
|
+
* so that all values sum to 1. Returns `undefined` for an empty object.
|
|
37
|
+
*/
|
|
38
|
+
declare function normalize(weights: WeightMap): WeightMap | undefined;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { deck, normalize, pick, shuffle };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region src/index.d.ts
|
|
2
|
+
type WeightMap = Record<string, number>;
|
|
3
|
+
/**
|
|
4
|
+
* Binds `shuffle` and `pick` to the given collection, returning an object
|
|
5
|
+
* with both methods pre-applied to `xs`.
|
|
6
|
+
*/
|
|
7
|
+
declare function deck<T>(xs: readonly T[]): {
|
|
8
|
+
shuffle: () => T[];
|
|
9
|
+
pick: () => T | undefined;
|
|
10
|
+
};
|
|
11
|
+
declare function deck(xs: WeightMap): {
|
|
12
|
+
shuffle: () => string[];
|
|
13
|
+
pick: () => string | undefined;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Returns a shuffled copy of `xs` without mutating the original.
|
|
17
|
+
*
|
|
18
|
+
* If `xs` is an Array, elements are reordered with uniform probability.
|
|
19
|
+
* If `xs` is an Object, returns a shuffled array of its keys weighted by
|
|
20
|
+
* their values.
|
|
21
|
+
*/
|
|
22
|
+
declare function shuffle<T>(xs: readonly T[]): T[];
|
|
23
|
+
declare function shuffle(xs: WeightMap): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Samples a single element from `xs` without mutating it.
|
|
26
|
+
*
|
|
27
|
+
* If `xs` is an Array, returns a uniformly random element, or `undefined` if
|
|
28
|
+
* the array is empty.
|
|
29
|
+
* If `xs` is an Object, returns a key sampled with probability proportional to
|
|
30
|
+
* its normalized weight, or `undefined` if the object is empty.
|
|
31
|
+
*/
|
|
32
|
+
declare function pick<T>(xs: readonly T[]): T | undefined;
|
|
33
|
+
declare function pick(xs: WeightMap): string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Returns a copy of `weights` where every value has been divided by the total
|
|
36
|
+
* so that all values sum to 1. Returns `undefined` for an empty object.
|
|
37
|
+
*/
|
|
38
|
+
declare function normalize(weights: WeightMap): WeightMap | undefined;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { deck, normalize, pick, shuffle };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { isPlainObject } from "@thi.ng/checks";
|
|
2
|
+
import random from "alea-random";
|
|
3
|
+
//#region src/index.ts
|
|
4
|
+
function deck(xs) {
|
|
5
|
+
if (!(Array.isArray(xs) || isPlainObject(xs))) throw new TypeError("Must be an Array or an Object");
|
|
6
|
+
return {
|
|
7
|
+
shuffle: shuffle.bind(null, xs),
|
|
8
|
+
pick: pick.bind(null, xs)
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function shuffle(xs) {
|
|
12
|
+
if (Array.isArray(xs)) {
|
|
13
|
+
const res = xs.slice();
|
|
14
|
+
for (let i = res.length - 1; i >= 0; i--) {
|
|
15
|
+
const n = Math.floor(random(0, 1, true) * i);
|
|
16
|
+
const t = res[i];
|
|
17
|
+
res[i] = res[n];
|
|
18
|
+
res[n] = t;
|
|
19
|
+
}
|
|
20
|
+
return res;
|
|
21
|
+
}
|
|
22
|
+
if (isPlainObject(xs)) {
|
|
23
|
+
const weights = { ...xs };
|
|
24
|
+
const ret = [];
|
|
25
|
+
while (Object.keys(weights).length > 0) {
|
|
26
|
+
const key = pick(weights);
|
|
27
|
+
delete weights[key];
|
|
28
|
+
ret.push(key);
|
|
29
|
+
}
|
|
30
|
+
return ret;
|
|
31
|
+
}
|
|
32
|
+
throw new TypeError("Must be an Array or an Object");
|
|
33
|
+
}
|
|
34
|
+
function pick(xs) {
|
|
35
|
+
if (Array.isArray(xs)) {
|
|
36
|
+
if (xs.length === 0) return;
|
|
37
|
+
return xs[Math.floor(random(0, 1, true) * xs.length)];
|
|
38
|
+
}
|
|
39
|
+
if (isPlainObject(xs)) {
|
|
40
|
+
const weights = normalize(xs);
|
|
41
|
+
if (!weights) return;
|
|
42
|
+
const n = random(0, 1, true);
|
|
43
|
+
let threshold = 0;
|
|
44
|
+
const keyz = Object.keys(weights);
|
|
45
|
+
for (const key of keyz) {
|
|
46
|
+
threshold += weights[key];
|
|
47
|
+
if (n < threshold) return key;
|
|
48
|
+
}
|
|
49
|
+
throw new Error("Exceeded threshold. Something is very wrong.");
|
|
50
|
+
}
|
|
51
|
+
throw new TypeError("Must be an Array or an Object");
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Returns a copy of `weights` where every value has been divided by the total
|
|
55
|
+
* so that all values sum to 1. Returns `undefined` for an empty object.
|
|
56
|
+
*/
|
|
57
|
+
function normalize(weights) {
|
|
58
|
+
if (!isPlainObject(weights)) throw new TypeError("`weights` must be an object");
|
|
59
|
+
const keyz = Object.keys(weights);
|
|
60
|
+
if (!keyz.length) return;
|
|
61
|
+
const total = keyz.reduce((sum, key) => {
|
|
62
|
+
const x = weights[key];
|
|
63
|
+
if (x < 0) throw new Error(`Negative weight encountered at key ${key}`);
|
|
64
|
+
if (typeof x !== "number" || Number.isNaN(x)) throw new TypeError(`Number expected, got ${typeof x}`);
|
|
65
|
+
return sum + x;
|
|
66
|
+
}, 0);
|
|
67
|
+
return total === 1 ? weights : keyz.reduce((acc, key) => {
|
|
68
|
+
acc[key] = weights[key] / total;
|
|
69
|
+
return acc;
|
|
70
|
+
}, {});
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
export { deck, normalize, pick, shuffle };
|
package/package.json
CHANGED
|
@@ -1,49 +1,64 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "alea-deck",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"description": "Uniform and weighted shuffling and sampling with Alea",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"alea",
|
|
7
7
|
"math",
|
|
8
8
|
"random"
|
|
9
9
|
],
|
|
10
|
-
"repository": "github:kenany/alea-deck",
|
|
11
10
|
"license": "MIT",
|
|
12
11
|
"author": "Kenan Yildirim <kenan@kenany.me> (https://kenany.me/)",
|
|
13
|
-
"
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/kenany/alea-deck.git"
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"type": "commonjs",
|
|
20
|
+
"types": "dist/index.d.cts",
|
|
21
|
+
"main": "dist/index.cjs",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"import": "./dist/index.mjs",
|
|
25
|
+
"require": "./dist/index.cjs"
|
|
26
|
+
},
|
|
27
|
+
"./*.mjs": {
|
|
28
|
+
"default": "./dist/*.mjs"
|
|
29
|
+
},
|
|
30
|
+
"./*.cjs": {
|
|
31
|
+
"default": "./dist/*.cjs"
|
|
32
|
+
},
|
|
33
|
+
"./*": {
|
|
34
|
+
"import": "./dist/*.mjs",
|
|
35
|
+
"require": "./dist/*.cjs"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
14
38
|
"files": [
|
|
15
|
-
"
|
|
39
|
+
"dist",
|
|
16
40
|
"LICENSE.txt"
|
|
17
41
|
],
|
|
18
|
-
"directories": {
|
|
19
|
-
"test": "test"
|
|
20
|
-
},
|
|
21
42
|
"engines": {
|
|
22
|
-
"node": "
|
|
23
|
-
},
|
|
24
|
-
"scripts": {
|
|
25
|
-
"lint": "eslint *.js test/*.js",
|
|
26
|
-
"test": "tape test/*.js",
|
|
27
|
-
"posttest": "npm run lint",
|
|
28
|
-
"release": "semantic-release"
|
|
43
|
+
"node": "22 || 24 || >=26"
|
|
29
44
|
},
|
|
30
45
|
"dependencies": {
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"lodash.isplainobject": "^4.0.6",
|
|
34
|
-
"lodash.keys": "^4.2.0",
|
|
35
|
-
"lodash.reduce": "^4.6.0"
|
|
46
|
+
"@thi.ng/checks": "^3.10.0",
|
|
47
|
+
"alea-random": "^6.0.1"
|
|
36
48
|
},
|
|
37
49
|
"devDependencies": {
|
|
38
|
-
"@
|
|
39
|
-
"@semantic-release
|
|
40
|
-
"@
|
|
41
|
-
"conventional-changelog-conventionalcommits": "
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
"@biomejs/biome": "2.4.16",
|
|
51
|
+
"@containerbase/semantic-release-pnpm": "1.4.1",
|
|
52
|
+
"@kenan/biome-config": "1.0.6",
|
|
53
|
+
"conventional-changelog-conventionalcommits": "9.3.1",
|
|
54
|
+
"semantic-release": "25.0.3",
|
|
55
|
+
"tsdown": "0.22.2",
|
|
56
|
+
"typescript": "6.0.3",
|
|
57
|
+
"vitest": "4.1.8"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsdown && tsc --noEmit",
|
|
61
|
+
"lint": "biome check",
|
|
62
|
+
"test": "vitest run"
|
|
48
63
|
}
|
|
49
|
-
}
|
|
64
|
+
}
|
package/index.js
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
const random = require('alea-random');
|
|
2
|
-
const isPlainObject = require('lodash.isplainobject');
|
|
3
|
-
const keys = require('lodash.keys');
|
|
4
|
-
const reduce = require('lodash.reduce');
|
|
5
|
-
const isNumber = require('lodash.isnumber');
|
|
6
|
-
|
|
7
|
-
function deck(xs) {
|
|
8
|
-
if (!Array.isArray(xs) && !isPlainObject(xs)) {
|
|
9
|
-
throw new TypeError('Must be an Array or an Object');
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
return reduce(keys(module.exports), function(acc, name) {
|
|
13
|
-
acc[name] = module.exports[name].bind(null, xs);
|
|
14
|
-
return acc;
|
|
15
|
-
}, {});
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function shuffle(xs) {
|
|
19
|
-
if (Array.isArray(xs)) {
|
|
20
|
-
const res = xs.slice();
|
|
21
|
-
for (let i = res.length - 1; i >= 0; i--) {
|
|
22
|
-
const n = Math.floor(random(true) * i);
|
|
23
|
-
const t = res[i];
|
|
24
|
-
res[i] = res[n];
|
|
25
|
-
res[n] = t;
|
|
26
|
-
}
|
|
27
|
-
return res;
|
|
28
|
-
}
|
|
29
|
-
else if (isPlainObject(xs)) {
|
|
30
|
-
const weights = reduce(keys(xs), function(acc, key) {
|
|
31
|
-
acc[key] = xs[key];
|
|
32
|
-
return acc;
|
|
33
|
-
}, {});
|
|
34
|
-
|
|
35
|
-
const ret = [];
|
|
36
|
-
|
|
37
|
-
while (keys(weights).length > 0) {
|
|
38
|
-
const key = pick(weights);
|
|
39
|
-
delete weights[key];
|
|
40
|
-
ret.push(key);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return ret;
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
throw new TypeError('Must be an Array or an Object');
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function pick(xs) {
|
|
51
|
-
if (Array.isArray(xs)) {
|
|
52
|
-
return xs[Math.floor(random(true) * xs.length)];
|
|
53
|
-
}
|
|
54
|
-
else if (isPlainObject(xs)) {
|
|
55
|
-
const weights = normalize(xs);
|
|
56
|
-
if (!weights) {
|
|
57
|
-
return undefined;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const n = random(true);
|
|
61
|
-
let threshold = 0;
|
|
62
|
-
const keyz = keys(weights);
|
|
63
|
-
|
|
64
|
-
for (let i = 0; i < keyz.length; i++) {
|
|
65
|
-
threshold += weights[keyz[i]];
|
|
66
|
-
if (n < threshold) {
|
|
67
|
-
return keyz[i];
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
throw new Error('Exceeded threshold. Something is very wrong.');
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
throw new TypeError('Must be an Array or an Object');
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function normalize(weights) {
|
|
78
|
-
if (!isPlainObject(weights)) {
|
|
79
|
-
throw new TypeError('`weights` must be an object');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const keyz = keys(weights);
|
|
83
|
-
if (!keyz.length) {
|
|
84
|
-
return undefined;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const total = reduce(keyz, function(sum, key) {
|
|
88
|
-
const x = weights[key];
|
|
89
|
-
if (x < 0) {
|
|
90
|
-
throw new Error('Negative weight encountered at key ' + key);
|
|
91
|
-
}
|
|
92
|
-
else if (!isNumber(x)) {
|
|
93
|
-
throw new TypeError('Number expected, got ' + typeof x);
|
|
94
|
-
}
|
|
95
|
-
else {
|
|
96
|
-
return sum + x;
|
|
97
|
-
}
|
|
98
|
-
}, 0);
|
|
99
|
-
|
|
100
|
-
return total === 1
|
|
101
|
-
? weights
|
|
102
|
-
: reduce(keyz, function(acc, key) {
|
|
103
|
-
acc[key] = weights[key] / total;
|
|
104
|
-
return acc;
|
|
105
|
-
}, {});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
module.exports = deck;
|
|
109
|
-
module.exports.shuffle = shuffle;
|
|
110
|
-
module.exports.pick = pick;
|
|
111
|
-
module.exports.normalize = normalize;
|