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 CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2014–2023 Kenan Yildirim <https://kenany.me/>
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
- var deck = require('alea-deck');
10
+ import { shuffle, pick } from 'alea-deck';
11
11
 
12
- deck.shuffle([1, 2, 3, 4]);
12
+ shuffle([1, 2, 3, 4]);
13
13
  // => [1, 4, 2, 3]
14
14
 
15
- deck.pick([1, 2, 3, 4]);
15
+ pick([1, 2, 3, 4]);
16
16
  // => 2
17
17
 
18
- deck.shuffle({
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
- deck.pick({
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
- var deck = require('alea-deck');
46
+ import { deck, shuffle, pick, normalize } from 'alea-deck';
47
47
  ```
48
48
 
49
- ### `deck.shuffle(collection)`
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
- ### `deck.pick(collection)`
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
- ### `deck.normalize(obj)`
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;
@@ -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 };
@@ -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": "4.1.4",
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
- "main": "index.js",
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
- "index.js",
39
+ "dist",
16
40
  "LICENSE.txt"
17
41
  ],
18
- "directories": {
19
- "test": "test"
20
- },
21
42
  "engines": {
22
- "node": "18 || >=20"
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
- "alea-random": "^5.0.4",
32
- "lodash.isnumber": "^3.0.3",
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
- "@kenan/eslint-config": "^11.1.11",
39
- "@semantic-release/changelog": "^6.0.3",
40
- "@semantic-release/git": "^10.0.1",
41
- "conventional-changelog-conventionalcommits": "^8.0.0",
42
- "eslint": "^8.57.1",
43
- "lodash.every": "^4.6.0",
44
- "lodash.isundefined": "^3.0.1",
45
- "lodash.map": "^4.6.0",
46
- "semantic-release": "^24.2.3",
47
- "tape": "^5.9.0"
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;