atomic-fns 1.1.7 → 2.0.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.
Files changed (216) hide show
  1. package/README.md +405 -60
  2. package/dist/array/index.d.ts +9 -0
  3. package/dist/array/index.js +9 -0
  4. package/dist/array/list.d.ts +99 -0
  5. package/dist/array/list.js +133 -0
  6. package/dist/array/sets.d.ts +130 -0
  7. package/dist/array/sets.js +213 -0
  8. package/dist/async/index.d.ts +6 -0
  9. package/dist/async/index.js +6 -0
  10. package/dist/async/retry.d.ts +38 -0
  11. package/dist/async/retry.js +56 -0
  12. package/dist/async/sleep.d.ts +42 -0
  13. package/dist/async/sleep.js +76 -0
  14. package/dist/collections/bisect.d.ts +103 -0
  15. package/dist/collections/bisect.js +157 -0
  16. package/dist/collections/deque.d.ts +67 -97
  17. package/dist/collections/deque.js +197 -249
  18. package/dist/collections/entries.d.ts +89 -0
  19. package/dist/collections/entries.js +131 -0
  20. package/dist/collections/frozen-set.d.ts +42 -0
  21. package/dist/collections/frozen-set.js +64 -0
  22. package/dist/collections/group.d.ts +141 -0
  23. package/dist/collections/group.js +241 -0
  24. package/dist/collections/heap.d.ts +64 -0
  25. package/dist/collections/heap.js +205 -0
  26. package/dist/collections/index.d.ts +15 -642
  27. package/dist/collections/index.js +14 -716
  28. package/dist/collections/internal/arrayKeys.d.ts +19 -0
  29. package/dist/collections/internal/arrayKeys.js +83 -0
  30. package/dist/collections/internal/invariants.d.ts +4 -0
  31. package/dist/collections/internal/invariants.js +12 -0
  32. package/dist/collections/internal/iterable.d.ts +25 -0
  33. package/dist/collections/internal/iterable.js +62 -0
  34. package/dist/collections/iterate.d.ts +162 -0
  35. package/dist/collections/iterate.js +188 -0
  36. package/dist/collections/lru-cache.d.ts +58 -0
  37. package/dist/collections/lru-cache.js +164 -0
  38. package/dist/collections/order.d.ts +19 -0
  39. package/dist/collections/order.js +38 -0
  40. package/dist/collections/shape.d.ts +32 -0
  41. package/dist/collections/shape.js +86 -0
  42. package/dist/collections/sorted-map.d.ts +171 -0
  43. package/dist/collections/sorted-map.js +845 -0
  44. package/dist/collections/sorted-set.d.ts +103 -0
  45. package/dist/collections/sorted-set.js +147 -0
  46. package/dist/collections/splay-tree.d.ts +82 -0
  47. package/dist/collections/splay-tree.js +354 -0
  48. package/dist/collections/trie.d.ts +58 -0
  49. package/dist/collections/trie.js +196 -0
  50. package/dist/decimal/currency.d.ts +211 -0
  51. package/dist/decimal/currency.js +321 -0
  52. package/dist/decimal/decimal.d.ts +299 -0
  53. package/dist/decimal/decimal.js +660 -0
  54. package/dist/decimal/index.d.ts +11 -145
  55. package/dist/decimal/index.js +10 -374
  56. package/dist/decimal/internal/bits.d.ts +2 -0
  57. package/dist/decimal/internal/bits.js +7 -0
  58. package/dist/decimal/internal/echo.d.ts +4 -0
  59. package/dist/decimal/internal/echo.js +6 -0
  60. package/dist/decimal/internal/isqrt.d.ts +9 -0
  61. package/dist/decimal/internal/isqrt.js +34 -0
  62. package/dist/decimal/internal/locale.d.ts +7 -0
  63. package/dist/decimal/internal/locale.js +24 -0
  64. package/dist/decimal/internal/minor-units.d.ts +2 -0
  65. package/dist/decimal/internal/minor-units.js +33 -0
  66. package/dist/decimal/internal/pow10.d.ts +24 -0
  67. package/dist/decimal/internal/pow10.js +96 -0
  68. package/dist/encoding/base64.d.ts +39 -0
  69. package/dist/encoding/base64.js +53 -0
  70. package/dist/encoding/hex.d.ts +23 -0
  71. package/dist/encoding/hex.js +34 -0
  72. package/dist/encoding/index.d.ts +6 -0
  73. package/dist/encoding/index.js +6 -0
  74. package/dist/function/debounce.d.ts +68 -0
  75. package/dist/function/debounce.js +203 -0
  76. package/dist/function/index.d.ts +6 -0
  77. package/dist/function/index.js +6 -0
  78. package/dist/function/shared.d.ts +2 -0
  79. package/dist/function/shared.js +5 -0
  80. package/dist/function/wrap.d.ts +130 -0
  81. package/dist/function/wrap.js +113 -0
  82. package/dist/hash/digest.d.ts +18 -0
  83. package/dist/hash/digest.js +26 -0
  84. package/dist/hash/hash.d.ts +22 -0
  85. package/dist/hash/hash.js +252 -0
  86. package/dist/hash/hashString.d.ts +13 -0
  87. package/dist/hash/hashString.js +53 -0
  88. package/dist/hash/index.d.ts +8 -0
  89. package/dist/hash/index.js +8 -0
  90. package/dist/hash/stableStringify.d.ts +19 -0
  91. package/dist/hash/stableStringify.js +102 -0
  92. package/dist/id/index.d.ts +7 -0
  93. package/dist/id/index.js +7 -0
  94. package/dist/id/nanoid.d.ts +16 -0
  95. package/dist/id/nanoid.js +65 -0
  96. package/dist/id/uniqueId.d.ts +14 -0
  97. package/dist/id/uniqueId.js +20 -0
  98. package/dist/id/uuid.d.ts +25 -0
  99. package/dist/id/uuid.js +64 -0
  100. package/dist/index.d.ts +15 -8
  101. package/dist/index.js +15 -8
  102. package/dist/internal/bytes.d.ts +11 -0
  103. package/dist/internal/bytes.js +33 -0
  104. package/dist/internal/codec.d.ts +11 -0
  105. package/dist/internal/codec.js +84 -0
  106. package/dist/internal/compare.d.ts +10 -0
  107. package/dist/internal/compare.js +26 -0
  108. package/dist/internal/crypto.d.ts +5 -0
  109. package/dist/internal/crypto.js +11 -0
  110. package/dist/internal/keys.d.ts +12 -0
  111. package/dist/internal/keys.js +23 -0
  112. package/dist/internal/kind.d.ts +44 -0
  113. package/dist/internal/kind.js +139 -0
  114. package/dist/internal/sparse.d.ts +51 -0
  115. package/dist/internal/sparse.js +104 -0
  116. package/dist/internal/text.d.ts +7 -0
  117. package/dist/internal/text.js +15 -0
  118. package/dist/internal/timer.d.ts +10 -0
  119. package/dist/internal/timer.js +17 -0
  120. package/dist/internal/truncate.d.ts +10 -0
  121. package/dist/internal/truncate.js +22 -0
  122. package/dist/internal/walk.d.ts +47 -0
  123. package/dist/internal/walk.js +100 -0
  124. package/dist/is/index.d.ts +8 -0
  125. package/dist/is/index.js +8 -0
  126. package/dist/is/isArrayLike.d.ts +18 -0
  127. package/dist/is/isArrayLike.js +25 -0
  128. package/dist/is/isError.d.ts +13 -0
  129. package/dist/is/isError.js +18 -0
  130. package/dist/is/isNil.d.ts +12 -0
  131. package/dist/is/isNil.js +14 -0
  132. package/dist/is/isPlainObject.d.ts +15 -0
  133. package/dist/is/isPlainObject.js +34 -0
  134. package/dist/number/index.d.ts +7 -0
  135. package/dist/number/index.js +7 -0
  136. package/dist/number/round.d.ts +86 -0
  137. package/dist/number/round.js +130 -0
  138. package/dist/number/stats.d.ts +52 -0
  139. package/dist/number/stats.js +115 -0
  140. package/dist/object/cloneDeep.d.ts +28 -0
  141. package/dist/object/cloneDeep.js +200 -0
  142. package/dist/object/index.d.ts +14 -0
  143. package/dist/object/index.js +13 -0
  144. package/dist/object/isEqual.d.ts +64 -0
  145. package/dist/object/isEqual.js +289 -0
  146. package/dist/object/matches.d.ts +40 -0
  147. package/dist/object/matches.js +284 -0
  148. package/dist/object/merge.d.ts +66 -0
  149. package/dist/object/merge.js +272 -0
  150. package/dist/object/parsePath.d.ts +22 -0
  151. package/dist/object/parsePath.js +111 -0
  152. package/dist/object/path.d.ts +85 -0
  153. package/dist/object/path.js +208 -0
  154. package/dist/object/pick.d.ts +91 -0
  155. package/dist/object/pick.js +180 -0
  156. package/dist/object/reshape.d.ts +73 -0
  157. package/dist/object/reshape.js +144 -0
  158. package/dist/random/index.d.ts +96 -32
  159. package/dist/random/index.js +215 -50
  160. package/dist/string/case.d.ts +71 -0
  161. package/dist/string/case.js +152 -0
  162. package/dist/string/fold.d.ts +35 -0
  163. package/dist/string/fold.js +100 -0
  164. package/dist/string/index.d.ts +6 -187
  165. package/dist/string/index.js +6 -226
  166. package/dist/string/truncate.d.ts +25 -0
  167. package/dist/string/truncate.js +44 -0
  168. package/dist/string/words.d.ts +27 -0
  169. package/dist/string/words.js +241 -0
  170. package/package.json +101 -58
  171. package/dist/collections/BTree.d.ts +0 -229
  172. package/dist/collections/BTree.js +0 -1165
  173. package/dist/collections/Heap.d.ts +0 -69
  174. package/dist/collections/Heap.js +0 -181
  175. package/dist/collections/LRUCache.d.ts +0 -80
  176. package/dist/collections/LRUCache.js +0 -126
  177. package/dist/collections/SortedMap.d.ts +0 -92
  178. package/dist/collections/SortedMap.js +0 -131
  179. package/dist/collections/SortedSet.d.ts +0 -69
  180. package/dist/collections/SortedSet.js +0 -99
  181. package/dist/collections/SortedTree.d.ts +0 -103
  182. package/dist/collections/SortedTree.js +0 -141
  183. package/dist/collections/SplayTree.d.ts +0 -105
  184. package/dist/collections/SplayTree.js +0 -338
  185. package/dist/collections/Trie.d.ts +0 -42
  186. package/dist/collections/Trie.js +0 -104
  187. package/dist/collections/abc.d.ts +0 -152
  188. package/dist/collections/abc.js +0 -39
  189. package/dist/collections/frozenset.d.ts +0 -19
  190. package/dist/collections/frozenset.js +0 -30
  191. package/dist/functools/index.d.ts +0 -130
  192. package/dist/functools/index.js +0 -235
  193. package/dist/globals/index.d.ts +0 -285
  194. package/dist/globals/index.js +0 -463
  195. package/dist/i18n/currency.d.ts +0 -24
  196. package/dist/i18n/currency.js +0 -85
  197. package/dist/i18n/date/index.d.ts +0 -203
  198. package/dist/i18n/date/index.js +0 -531
  199. package/dist/i18n/date/utils.d.ts +0 -65
  200. package/dist/i18n/date/utils.js +0 -290
  201. package/dist/i18n/duration.d.ts +0 -112
  202. package/dist/i18n/duration.js +0 -392
  203. package/dist/i18n/format.d.ts +0 -31
  204. package/dist/i18n/format.js +0 -506
  205. package/dist/i18n/index.d.ts +0 -8
  206. package/dist/i18n/index.js +0 -8
  207. package/dist/itertools/index.d.ts +0 -255
  208. package/dist/itertools/index.js +0 -437
  209. package/dist/math/index.d.ts +0 -96
  210. package/dist/math/index.js +0 -98
  211. package/dist/mod.d.ts +0 -1
  212. package/dist/mod.js +0 -1
  213. package/dist/operators/index.d.ts +0 -152
  214. package/dist/operators/index.js +0 -305
  215. package/dist/types.d.ts +0 -19
  216. package/dist/types.js +0 -1
@@ -0,0 +1,73 @@
1
+ /**
2
+ * New objects from old ones: `flattenObject`, `invert`, `mapKeys`, `mapValues`.
3
+ */
4
+ /** Options of {@link flattenObject}. */
5
+ export interface FlattenOptions {
6
+ /** What joins the keys of nested objects; `'.'` by default. Array indices are written `[0]`. */
7
+ delimiter?: string;
8
+ }
9
+ /**
10
+ * A one-level object whose keys are the paths to the leaves of `obj`: nested plain-object keys
11
+ * joined by `delimiter`, array elements as `[index]`, so with the default delimiter every key is a
12
+ * path {@link get} reads. An own key of an array that is not an index (the `index` of a RegExp
13
+ * match) is joined by `delimiter` like an object key. A leaf is any value that is not a plain object
14
+ * or array (`null`, a `Date` and class instances included); empty containers give no key.
15
+ * @param obj A plain object or array.
16
+ * @param options `delimiter`, `'.'` by default.
17
+ * @returns A new plain object.
18
+ * @throws {TypeError} when `delimiter` is not a non-empty string, or `obj` is not a plain object or
19
+ * array; for a cycle; for a key that is empty or holds the delimiter, `[` or `]`, since its path
20
+ * would read as another one; and when two paths flatten to the same key (a delimiter of several
21
+ * characters can join different keys alike), which is the same unrepresentable-key case.
22
+ * @throws {RangeError} past 1000 levels of nesting, when shared objects repeat past 250000 units
23
+ * of work, or for output keys over 2^27 characters in all.
24
+ * @example
25
+ * ```ts
26
+ * flattenObject({ a: { b: 1, c: [2, { d: 3 }] } }) // { 'a.b': 1, 'a.c[0]': 2, 'a.c[1].d': 3 }
27
+ * flattenObject({ a: { b: 1 } }, { delimiter: '/' }) // { 'a/b': 1 }
28
+ * ```
29
+ */
30
+ export declare function flattenObject(obj: object, options?: FlattenOptions): Record<string, unknown>;
31
+ /**
32
+ * A new object whose keys are the values of `obj` and whose values are its keys, as lodash's
33
+ * `invert` builds it; a later key wins when two values are the same. A primitive value becomes a
34
+ * key as property keys do, so `1` and `'1'` are one key, and a symbol stays a symbol key.
35
+ * @param obj The object to read; its own enumerable string keys. `null` and `undefined` give `{}`.
36
+ * @returns A new plain object.
37
+ * @throws {TypeError} for a value that is an object or a function, since turning it into a key
38
+ * would call its own `toString`.
39
+ * @example
40
+ * ```ts
41
+ * invert({ a: 'x', b: 'y' }) // { x: 'a', y: 'b' }
42
+ * ```
43
+ */
44
+ export declare function invert(obj: object | null | undefined): Record<PropertyKey, string>;
45
+ /**
46
+ * A new object with the values of `obj` under the keys `keyOf` returns.
47
+ * @param obj The object to read; its own enumerable string keys. `null` and `undefined` give `{}`.
48
+ * @param keyOf Called with `(value, key, obj)`; returns the new key.
49
+ * @returns A new plain object; a later entry wins when two keys are the same.
50
+ * @example
51
+ * ```ts
52
+ * mapKeys({ a: 1, b: 2 }, (_value, key) => key.toUpperCase()) // { A: 1, B: 2 }
53
+ * ```
54
+ */
55
+ export declare function mapKeys<V, K extends PropertyKey>(obj: readonly V[] | null | undefined, keyOf: (value: V, key: string, obj: readonly V[]) => K): Record<K, V>;
56
+ /** {@link mapKeys} for an object: `keyOf` gets each own enumerable string key and its value. */
57
+ export declare function mapKeys<T extends object, K extends PropertyKey>(obj: T | null | undefined, keyOf: (value: T[keyof T], key: keyof T & string, obj: T) => K): Record<K, T[keyof T]>;
58
+ /**
59
+ * A new object with the keys of `obj` and the values `valueOf` returns, as lodash's `mapValues`
60
+ * builds it. For an array the result is a plain object keyed by index, not an array.
61
+ * @param obj The object to read; its own enumerable string keys. `null` and `undefined` give `{}`.
62
+ * @param valueOf Called with `(value, key, obj)`; returns the new value.
63
+ * @returns A new plain object.
64
+ * @example
65
+ * ```ts
66
+ * mapValues({ a: 1, b: 2 }, (value) => value * 10) // { a: 10, b: 20 }
67
+ * ```
68
+ */
69
+ export declare function mapValues<V, R>(obj: readonly V[] | null | undefined, valueOf: (value: V, key: string, obj: readonly V[]) => R): Record<number, R>;
70
+ /** {@link mapValues} for an object: the result keeps its own enumerable string keys. */
71
+ export declare function mapValues<T extends object, R>(obj: T | null | undefined, valueOf: (value: T[keyof T], key: keyof T & string, obj: T) => R): {
72
+ [K in keyof T as K extends symbol ? never : K]: R;
73
+ };
@@ -0,0 +1,144 @@
1
+ /**
2
+ * New objects from old ones: `flattenObject`, `invert`, `mapKeys`, `mapValues`.
3
+ */
4
+ import { isPlainObject } from '../is/isPlainObject.js';
5
+ import { isCanonicalArrayIndex } from '../internal/sparse.js';
6
+ import { truncateForError } from '../internal/truncate.js';
7
+ import { Ancestors, guardDepth, RevisitBudget } from '../internal/walk.js';
8
+ import { writeKey } from './parsePath.js';
9
+ const FLATTEN_BUDGET = 250_000;
10
+ const MAX_KEY_CHARS = 2 ** 27;
11
+ /**
12
+ * A one-level object whose keys are the paths to the leaves of `obj`: nested plain-object keys
13
+ * joined by `delimiter`, array elements as `[index]`, so with the default delimiter every key is a
14
+ * path {@link get} reads. An own key of an array that is not an index (the `index` of a RegExp
15
+ * match) is joined by `delimiter` like an object key. A leaf is any value that is not a plain object
16
+ * or array (`null`, a `Date` and class instances included); empty containers give no key.
17
+ * @param obj A plain object or array.
18
+ * @param options `delimiter`, `'.'` by default.
19
+ * @returns A new plain object.
20
+ * @throws {TypeError} when `delimiter` is not a non-empty string, or `obj` is not a plain object or
21
+ * array; for a cycle; for a key that is empty or holds the delimiter, `[` or `]`, since its path
22
+ * would read as another one; and when two paths flatten to the same key (a delimiter of several
23
+ * characters can join different keys alike), which is the same unrepresentable-key case.
24
+ * @throws {RangeError} past 1000 levels of nesting, when shared objects repeat past 250000 units
25
+ * of work, or for output keys over 2^27 characters in all.
26
+ * @example
27
+ * ```ts
28
+ * flattenObject({ a: { b: 1, c: [2, { d: 3 }] } }) // { 'a.b': 1, 'a.c[0]': 2, 'a.c[1].d': 3 }
29
+ * flattenObject({ a: { b: 1 } }, { delimiter: '/' }) // { 'a/b': 1 }
30
+ * ```
31
+ */
32
+ export function flattenObject(obj, options = {}) {
33
+ const delimiter = options.delimiter ?? '.';
34
+ if (typeof delimiter !== 'string' || delimiter === '') {
35
+ throw new TypeError('delimiter must be a non-empty string');
36
+ }
37
+ if (!Array.isArray(obj) && !isPlainObject(obj)) {
38
+ throw new TypeError('flattenObject takes a plain object or array');
39
+ }
40
+ return guardDepth(() => {
41
+ const out = {};
42
+ const ancestors = new Ancestors();
43
+ const budget = new RevisitBudget(FLATTEN_BUDGET, `input repeats shared objects beyond ${String(FLATTEN_BUDGET)} containers`);
44
+ const stack = [];
45
+ let chars = 0;
46
+ const emit = (key, value) => {
47
+ chars += key.length;
48
+ if (chars > MAX_KEY_CHARS)
49
+ throw new RangeError(`output keys exceed ${String(MAX_KEY_CHARS)} characters`);
50
+ if (Object.hasOwn(out, key))
51
+ throw new TypeError(`two paths flatten to ${truncateForError(key)}`);
52
+ writeKey(out, key, value);
53
+ };
54
+ const open = (container, prefix) => {
55
+ if (ancestors.has(container))
56
+ throw new TypeError(`cycle at ${truncateForError(prefix)}`);
57
+ const keys = Object.keys(container);
58
+ budget.note(container, keys.length);
59
+ ancestors.enter(container);
60
+ stack.push({ obj: container, prefix, keys, index: 0 });
61
+ };
62
+ try {
63
+ open(obj, '');
64
+ while (stack.length > 0) {
65
+ const frame = stack[stack.length - 1];
66
+ if (frame.index >= frame.keys.length) {
67
+ stack.pop();
68
+ ancestors.leave(frame.obj);
69
+ continue;
70
+ }
71
+ const key = frame.keys[frame.index++];
72
+ const isIndex = Array.isArray(frame.obj) && isCanonicalArrayIndex(key);
73
+ if (!isIndex &&
74
+ (key === '' || key.includes(delimiter) || key.includes('[') || key.includes(']'))) {
75
+ throw new TypeError(`flattenObject cannot represent the key ${truncateForError(key)}`);
76
+ }
77
+ const path = isIndex
78
+ ? `${frame.prefix}[${key}]`
79
+ : frame.prefix
80
+ ? frame.prefix + delimiter + key
81
+ : key;
82
+ const child = frame.obj[key];
83
+ if (Array.isArray(child) || isPlainObject(child))
84
+ open(child, path);
85
+ else
86
+ emit(path, child);
87
+ }
88
+ return out;
89
+ }
90
+ finally {
91
+ // A throw leaves frames open; leave them so a walk this one runs inside keeps its depth count.
92
+ for (let i = stack.length - 1; i >= 0; i--)
93
+ ancestors.leave(stack[i].obj);
94
+ }
95
+ });
96
+ }
97
+ /**
98
+ * A new object whose keys are the values of `obj` and whose values are its keys, as lodash's
99
+ * `invert` builds it; a later key wins when two values are the same. A primitive value becomes a
100
+ * key as property keys do, so `1` and `'1'` are one key, and a symbol stays a symbol key.
101
+ * @param obj The object to read; its own enumerable string keys. `null` and `undefined` give `{}`.
102
+ * @returns A new plain object.
103
+ * @throws {TypeError} for a value that is an object or a function, since turning it into a key
104
+ * would call its own `toString`.
105
+ * @example
106
+ * ```ts
107
+ * invert({ a: 'x', b: 'y' }) // { x: 'a', y: 'b' }
108
+ * ```
109
+ */
110
+ export function invert(obj) {
111
+ const out = {};
112
+ if (obj === null || obj === undefined)
113
+ return out;
114
+ for (const [key, value] of Object.entries(obj)) {
115
+ if ((typeof value === 'object' && value !== null) || typeof value === 'function') {
116
+ throw new TypeError(`invert cannot make a key of the value at ${truncateForError(key)}`);
117
+ }
118
+ writeKey(out, typeof value === 'symbol' ? value : String(value), key);
119
+ }
120
+ return out;
121
+ }
122
+ export function mapKeys(obj, keyOf) {
123
+ const out = {};
124
+ if (obj === null || obj === undefined)
125
+ return out;
126
+ const keyOfEntry = keyOf;
127
+ const record = obj;
128
+ for (const key of Object.keys(record)) {
129
+ const value = record[key];
130
+ writeKey(out, keyOfEntry(value, key, obj), value);
131
+ }
132
+ return out;
133
+ }
134
+ export function mapValues(obj, valueOf) {
135
+ const out = {};
136
+ if (obj === null || obj === undefined)
137
+ return out;
138
+ const valueOfEntry = valueOf;
139
+ const record = obj;
140
+ for (const key of Object.keys(record)) {
141
+ writeKey(out, key, valueOfEntry(record[key], key, obj));
142
+ }
143
+ return out;
144
+ }
@@ -1,42 +1,106 @@
1
+ import type { Collection, CollectionValue } from '../collections/shape.js';
2
+ /** The random source option every function in `fns/random` takes. */
3
+ export interface RandomOptions {
4
+ /** Returns numbers in `[0, 1)`, as `Math.random` does (the default). */
5
+ random?: () => number;
6
+ }
7
+ /** Options for {@link random}. */
8
+ export interface RandomNumberOptions extends RandomOptions {
9
+ /** Return a float in `[min, max)` even when both bounds are integers. */
10
+ float?: boolean;
11
+ }
1
12
  /**
2
- * This module implements pseudo-random generators for various distributions.
3
- *
4
- * @module random
13
+ * Returns a uniform random integer from `min` to `max`, both included, across any span of safe
14
+ * integers.
15
+ * @param min The lower bound (included).
16
+ * @param max The upper bound (included).
17
+ * @param options `random`, the source of randomness.
18
+ * @returns The integer.
19
+ * @throws {TypeError} when a bound is not a number, `random` is not a function or it returns a
20
+ * value that is not a number.
21
+ * @throws {RangeError} when a bound is not a safe integer, `max < min`, or `random` returns a
22
+ * number outside `[0, 1)`.
23
+ * @example
24
+ * ```ts
25
+ * randomInt(1, 6) // a die roll
26
+ * ```
5
27
  */
28
+ export declare function randomInt(min: number, max: number, options?: RandomOptions): number;
6
29
  /**
7
- * Returns a random float between a [min, max) value (max exclusive).
8
- * @param {number} min
9
- * @param {number} max
10
- * @returns {number}
30
+ * Returns a random number from `min` (default 0) to `max` (default 1), as lodash does: an integer
31
+ * with both bounds included, or a float in `[min, max)` when `float` is set or a bound is not a
32
+ * safe integer. One number is `max`.
33
+ * @param min The lower bound, or `max` when it is the only number.
34
+ * @param max The upper bound.
35
+ * @param options `float` and `random`.
36
+ * @returns The random number.
37
+ * @throws {TypeError} when a bound is not a number, `random` is not a function or it returns a
38
+ * value that is not a number.
39
+ * @throws {RangeError} when a bound is not finite, `max < min`, or `random` returns a number
40
+ * outside `[0, 1)`.
41
+ * @example
42
+ * ```ts
43
+ * random(5) // an integer from 0 to 5
44
+ * random(1.5, 2.5) // a float in [1.5, 2.5)
45
+ * random(0, 1, { float: true }) // a float in [0, 1)
46
+ * ```
11
47
  */
12
- export declare function random(min?: number, max?: number): number;
48
+ export declare function random(options?: RandomNumberOptions): number;
49
+ /** A random number from 0 to `max`. */
50
+ export declare function random(max: number, options?: RandomNumberOptions): number;
51
+ /** A random number from `min` to `max`. */
52
+ export declare function random(min: number, max: number, options?: RandomNumberOptions): number;
13
53
  /**
14
- * Returns a random integer between a [min, max] value (inclusive).
15
- * @param {number} min
16
- * @param {number} max
17
- * @returns {number}
54
+ * Returns a random value of a collection, or `undefined` when it has none. Collections follow the
55
+ * fns/collection shapes: an object gives its own enumerable values, an array its elements (holes
56
+ * skipped), a string its code points, a `Map` or `Set` its values, and any other iterable its
57
+ * items. Arrays and typed arrays read only selected positions unless an array hole triggers a
58
+ * full scan. Sparse arrays may consume extra draws, so their seeded results can differ from
59
+ * sampling their collected values.
60
+ * @param collection The collection to draw from.
61
+ * @param options `random`, the source of randomness.
62
+ * @returns The value drawn.
63
+ * @throws {TypeError} when `random` is not a function or returns a value that is not a number.
64
+ * @throws {RangeError} when `random` returns a number outside `[0, 1)`.
65
+ * @example
66
+ * ```ts
67
+ * sample([1, 2, 3]) // 1, 2 or 3
68
+ * sample({ a: 1, b: 2 }) // 1 or 2
69
+ * ```
18
70
  */
19
- export declare function randomInt(min?: number, max?: number): number;
71
+ export declare function sample<C extends Collection>(collection: C, options?: RandomOptions): CollectionValue<C> | undefined;
20
72
  /**
21
- * Returns the item at some random index in a sequence.
22
- * @returns {T}
23
- * @template T
73
+ * Returns `n` values of a collection at distinct positions, in random order, or every value when
74
+ * it has fewer than `n`. Sampling follows {@link sample}, including its collection shapes and
75
+ * sparse-array behavior. Full samples, and samples of at least five values covering at least half
76
+ * an array or typed array, copy its values before drawing. Smaller partial samples read only
77
+ * selected positions unless a missing position triggers a full scan. An `n` of 0 reads no values.
78
+ * @param collection The collection to draw from.
79
+ * @param n How many values to draw.
80
+ * @param options `random`, the source of randomness.
81
+ * @returns The values drawn.
82
+ * @throws {TypeError} when `n` is not a number, `random` is not a function or it returns a value
83
+ * that is not a number.
84
+ * @throws {RangeError} when `n` is not a non-negative integer or `random` returns a number outside
85
+ * `[0, 1)`.
86
+ * @example
87
+ * ```ts
88
+ * sampleSize([1, 2, 3], 2) // two of them, such as [3, 1]
89
+ * sampleSize([1, 2, 3], 4) // all three, shuffled
90
+ * ```
24
91
  */
25
- export declare function choice<T>(arr: T[]): T;
92
+ export declare function sampleSize<C extends Collection>(collection: C, n: number, options?: RandomOptions): CollectionValue<C>[];
26
93
  /**
27
- * Returns a random sample of size k from a list of items.
28
- * @param {Array} arr
29
- * @param {number} size
30
- * @returns A new array with `size` random elements from `arr`.
31
- * @template T
94
+ * Returns the values of a collection in random order, with a Fisher-Yates shuffle. It never
95
+ * changes `collection`; collections follow the shapes of {@link sample}.
96
+ * @param collection The collection to shuffle.
97
+ * @param options `random`, the source of randomness.
98
+ * @returns A new array of the values.
99
+ * @throws {TypeError} when `random` is not a function or returns a value that is not a number.
100
+ * @throws {RangeError} when `random` returns a number outside `[0, 1)`.
101
+ * @example
102
+ * ```ts
103
+ * shuffle([1, 2, 3, 4]) // such as [3, 1, 4, 2]
104
+ * ```
32
105
  */
33
- export declare function sample<T>(arr: T[], size: number): T[];
34
- /**
35
- * Shuffles a collection elements in-place. Uses the Fisher-Yates algorithm for uniform shuffling. If collection is an Object it returns shuffled `Object.values()`.
36
- *
37
- * @param {Array} arr
38
- * @param {number} size
39
- * @returns The shuffled array.
40
- * @template T
41
- */
42
- export declare function shuffle<T>(arr: T[], size?: number): T[];
106
+ export declare function shuffle<C extends Collection>(collection: C, options?: RandomOptions): CollectionValue<C>[];
@@ -1,69 +1,234 @@
1
1
  /**
2
- * This module implements pseudo-random generators for various distributions.
3
- *
2
+ * Random numbers and draws: `random`, `randomInt`, `sample`, `sampleSize` and `shuffle`. Every
3
+ * function uses `Math.random` unless given `{ random }`, a function that returns numbers in
4
+ * `[0, 1)`. `Math.random` is not cryptographically secure, so do not draw tokens, keys or anything
5
+ * secret with it: use `fns/id` or `crypto.getRandomValues` for those.
4
6
  * @module random
5
7
  */
6
- import { isObject } from '../globals/index.js';
8
+ import { values } from '../collections/entries.js';
9
+ import { isEnumerable } from '../internal/keys.js';
10
+ import { typedArrayLength } from '../internal/kind.js';
11
+ const TWO_32 = 2 ** 32;
12
+ const TWO_53 = 2 ** 53;
13
+ /** Redraws before accepting that a stubbed source keeps repeating a rejected draw. */
14
+ const MAX_DRAWS = 64;
15
+ function isRandomNumberOptions(x) {
16
+ return typeof x === 'object' && x !== null;
17
+ }
18
+ /** The source `options` names, checked on every draw against the `[0, 1)` contract. */
19
+ function sourceOf(options) {
20
+ const source = options?.random ?? Math.random;
21
+ if (typeof source !== 'function')
22
+ throw new TypeError('random must be a function');
23
+ return () => {
24
+ const r = source();
25
+ if (typeof r !== 'number')
26
+ throw new TypeError('random must return a number');
27
+ if (!(r >= 0 && r < 1))
28
+ throw new RangeError('random must return a number in [0, 1)');
29
+ return r;
30
+ };
31
+ }
32
+ /** The top 32 bits of one draw, which every source with at least 32 bits of precision fills. */
33
+ function draw32(source) {
34
+ return Math.floor(source() * TWO_32);
35
+ }
36
+ /** 53 random bits from two 32-bit chunks, so no bit depends on the precision of the source. */
37
+ function draw53(source) {
38
+ const high21 = Math.floor(draw32(source) / 2 ** 11);
39
+ return high21 * TWO_32 + draw32(source);
40
+ }
41
+ function uniformInt(min, max, source) {
42
+ // A span of up to 2^32 values takes one 32-bit draw, a wider one a 53-bit draw, and a span wider
43
+ // than 2^53 adds a coin flip for the top bit.
44
+ const narrow = max - min < TWO_32;
45
+ const wide = max - min >= TWO_53;
46
+ const range = narrow ? TWO_32 : TWO_53;
47
+ const span = wide ? TWO_53 : max - min + 1;
48
+ const bucket = (range - (range % span)) / span;
49
+ for (let draws = 0; draws < MAX_DRAWS; draws++) {
50
+ const draw = narrow ? draw32(source) : draw53(source);
51
+ const top = wide && source() >= 0.5 ? TWO_53 : 0;
52
+ const value = min + top + (draw - (draw % bucket)) / bucket;
53
+ if (value <= max)
54
+ return value;
55
+ }
56
+ return max;
57
+ }
58
+ /** Moves `size` uniformly drawn items to the front of `items` (Fisher-Yates) and keeps those. */
59
+ function drawInPlace(items, size, source) {
60
+ for (let i = 0; i < size && i < items.length - 1; i++) {
61
+ const j = uniformInt(i, items.length - 1, source);
62
+ [items[i], items[j]] = [items[j], items[i]];
63
+ }
64
+ items.length = size;
65
+ return items;
66
+ }
67
+ function indexedLength(collection) {
68
+ if (Array.isArray(collection))
69
+ return collection.length;
70
+ if (typeof collection === 'object' && collection !== null)
71
+ return typedArrayLength(collection);
72
+ return undefined;
73
+ }
74
+ function drawIndices(collection, length, size, source) {
75
+ const indices = [];
76
+ const displaced = new Map();
77
+ for (let i = 0; i < size; i++) {
78
+ const j = i < length - 1 ? uniformInt(i, length - 1, source) : i;
79
+ const index = displaced.get(j) ?? j;
80
+ if (Array.isArray(collection) && !isEnumerable(collection, index)) {
81
+ return undefined;
82
+ }
83
+ displaced.set(j, displaced.get(i) ?? i);
84
+ indices.push(index);
85
+ }
86
+ return indices;
87
+ }
7
88
  /**
8
- * Returns a random float between a [min, max) value (max exclusive).
9
- * @param {number} min
10
- * @param {number} max
11
- * @returns {number}
89
+ * Returns a uniform random integer from `min` to `max`, both included, across any span of safe
90
+ * integers.
91
+ * @param min The lower bound (included).
92
+ * @param max The upper bound (included).
93
+ * @param options `random`, the source of randomness.
94
+ * @returns The integer.
95
+ * @throws {TypeError} when a bound is not a number, `random` is not a function or it returns a
96
+ * value that is not a number.
97
+ * @throws {RangeError} when a bound is not a safe integer, `max < min`, or `random` returns a
98
+ * number outside `[0, 1)`.
99
+ * @example
100
+ * ```ts
101
+ * randomInt(1, 6) // a die roll
102
+ * ```
12
103
  */
13
- export function random(min = 0, max = 1) {
14
- return Math.random() * (max - min) + min;
104
+ export function randomInt(min, max, options) {
105
+ if (typeof min !== 'number' || typeof max !== 'number') {
106
+ throw new TypeError('min and max must be numbers');
107
+ }
108
+ if (!Number.isSafeInteger(min) || !Number.isSafeInteger(max)) {
109
+ throw new RangeError('min and max must be safe integers');
110
+ }
111
+ if (max < min)
112
+ throw new RangeError('max must be >= min');
113
+ return uniformInt(min, max, sourceOf(options));
15
114
  }
16
115
  /**
17
- * Returns a random integer between a [min, max] value (inclusive).
18
- * @param {number} min
19
- * @param {number} max
20
- * @returns {number}
116
+ * @param first `min`, `max`, or the options.
117
+ * @param second `max`, or the options.
118
+ * @param third The options.
21
119
  */
22
- export function randomInt(min = 0, max = 1) {
23
- return Math.round(random(min, max));
120
+ export function random(first, second, third) {
121
+ const options = [first, second, third].find(isRandomNumberOptions);
122
+ const bounds = [];
123
+ for (const bound of [first, second]) {
124
+ if (bound === undefined || isRandomNumberOptions(bound))
125
+ continue;
126
+ if (typeof bound !== 'number')
127
+ throw new TypeError('min and max must be numbers');
128
+ bounds.push(bound);
129
+ }
130
+ const [min, max] = bounds.length === 2 ? bounds : [0, bounds[0] ?? 1];
131
+ if (!Number.isFinite(min) || !Number.isFinite(max)) {
132
+ throw new RangeError('min and max must be finite');
133
+ }
134
+ if (max < min)
135
+ throw new RangeError('max must be >= min');
136
+ const source = sourceOf(options);
137
+ if (!options?.float && Number.isSafeInteger(min) && Number.isSafeInteger(max)) {
138
+ return uniformInt(min, max, source);
139
+ }
140
+ const r = source();
141
+ const value = min * (1 - r) + max * r;
142
+ return value >= min && value < max ? value : min;
24
143
  }
25
144
  /**
26
- * Returns the item at some random index in a sequence.
27
- * @returns {T}
28
- * @template T
145
+ * Returns a random value of a collection, or `undefined` when it has none. Collections follow the
146
+ * fns/collection shapes: an object gives its own enumerable values, an array its elements (holes
147
+ * skipped), a string its code points, a `Map` or `Set` its values, and any other iterable its
148
+ * items. Arrays and typed arrays read only selected positions unless an array hole triggers a
149
+ * full scan. Sparse arrays may consume extra draws, so their seeded results can differ from
150
+ * sampling their collected values.
151
+ * @param collection The collection to draw from.
152
+ * @param options `random`, the source of randomness.
153
+ * @returns The value drawn.
154
+ * @throws {TypeError} when `random` is not a function or returns a value that is not a number.
155
+ * @throws {RangeError} when `random` returns a number outside `[0, 1)`.
156
+ * @example
157
+ * ```ts
158
+ * sample([1, 2, 3]) // 1, 2 or 3
159
+ * sample({ a: 1, b: 2 }) // 1 or 2
160
+ * ```
29
161
  */
30
- export function choice(arr) {
31
- if (!arr?.length)
32
- return;
33
- return arr[randomInt(0, arr.length - 1)];
162
+ export function sample(collection, options) {
163
+ const source = sourceOf(options);
164
+ const length = indexedLength(collection);
165
+ if (length !== undefined) {
166
+ if (length === 0)
167
+ return undefined;
168
+ const index = uniformInt(0, length - 1, source);
169
+ if (!Array.isArray(collection) || isEnumerable(collection, index)) {
170
+ return collection[index];
171
+ }
172
+ }
173
+ const items = values(collection);
174
+ if (items.length === 0)
175
+ return undefined;
176
+ return items[uniformInt(0, items.length - 1, source)];
34
177
  }
35
178
  /**
36
- * Returns a random sample of size k from a list of items.
37
- * @param {Array} arr
38
- * @param {number} size
39
- * @returns A new array with `size` random elements from `arr`.
40
- * @template T
179
+ * Returns `n` values of a collection at distinct positions, in random order, or every value when
180
+ * it has fewer than `n`. Sampling follows {@link sample}, including its collection shapes and
181
+ * sparse-array behavior. Full samples, and samples of at least five values covering at least half
182
+ * an array or typed array, copy its values before drawing. Smaller partial samples read only
183
+ * selected positions unless a missing position triggers a full scan. An `n` of 0 reads no values.
184
+ * @param collection The collection to draw from.
185
+ * @param n How many values to draw.
186
+ * @param options `random`, the source of randomness.
187
+ * @returns The values drawn.
188
+ * @throws {TypeError} when `n` is not a number, `random` is not a function or it returns a value
189
+ * that is not a number.
190
+ * @throws {RangeError} when `n` is not a non-negative integer or `random` returns a number outside
191
+ * `[0, 1)`.
192
+ * @example
193
+ * ```ts
194
+ * sampleSize([1, 2, 3], 2) // two of them, such as [3, 1]
195
+ * sampleSize([1, 2, 3], 4) // all three, shuffled
196
+ * ```
41
197
  */
42
- export function sample(arr, size) {
43
- if (!arr?.length || arr.length < size)
198
+ export function sampleSize(collection, n, options) {
199
+ if (typeof n !== 'number')
200
+ throw new TypeError('n must be a number');
201
+ if (!Number.isSafeInteger(n) || n < 0)
202
+ throw new RangeError('n must be a non-negative integer');
203
+ const source = sourceOf(options);
204
+ if (n === 0)
44
205
  return [];
45
- return shuffle(arr.slice()).slice(-size);
206
+ const length = indexedLength(collection);
207
+ if (length !== undefined && n < length && (n < 5 || n < length / 2)) {
208
+ const indices = drawIndices(collection, length, Math.min(n, length), source);
209
+ if (indices !== undefined) {
210
+ const items = collection;
211
+ return indices.map((index) => items[index]);
212
+ }
213
+ }
214
+ const items = values(collection);
215
+ return drawInPlace(items, Math.min(n, items.length), source);
46
216
  }
47
217
  /**
48
- * Shuffles a collection elements in-place. Uses the Fisher-Yates algorithm for uniform shuffling. If collection is an Object it returns shuffled `Object.values()`.
49
- *
50
- * @param {Array} arr
51
- * @param {number} size
52
- * @returns The shuffled array.
53
- * @template T
218
+ * Returns the values of a collection in random order, with a Fisher-Yates shuffle. It never
219
+ * changes `collection`; collections follow the shapes of {@link sample}.
220
+ * @param collection The collection to shuffle.
221
+ * @param options `random`, the source of randomness.
222
+ * @returns A new array of the values.
223
+ * @throws {TypeError} when `random` is not a function or returns a value that is not a number.
224
+ * @throws {RangeError} when `random` returns a number outside `[0, 1)`.
225
+ * @example
226
+ * ```ts
227
+ * shuffle([1, 2, 3, 4]) // such as [3, 1, 4, 2]
228
+ * ```
54
229
  */
55
- export function shuffle(arr, size) {
56
- if (!arr?.length)
57
- return arr;
58
- if (isObject(arr))
59
- arr = Object.values(arr);
60
- if (size == null)
61
- size = arr.length - 1;
62
- for (let i = 0; i <= size; i++) {
63
- const j = randomInt(i, size);
64
- const temp = arr[j];
65
- arr[j] = arr[i];
66
- arr[i] = temp;
67
- }
68
- return arr;
230
+ export function shuffle(collection, options) {
231
+ const source = sourceOf(options);
232
+ const items = values(collection);
233
+ return drawInPlace(items, items.length, source);
69
234
  }