@byloth/core 2.2.2 → 2.2.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byloth/core",
3
- "version": "2.2.2",
3
+ "version": "2.2.3",
4
4
  "description": "An unopinionated collection of useful functions and classes that I use widely in all my projects. 🔧",
5
5
  "keywords": [
6
6
  "Core",
@@ -51,14 +51,14 @@
51
51
  "devDependencies": {
52
52
  "@byloth/eslint-config-typescript": "^3.2.2",
53
53
  "@eslint/compat": "^2.0.0",
54
- "@types/node": "^22.19.1",
55
- "@vitest/coverage-v8": "^4.0.12",
56
- "eslint": "^9.39.1",
54
+ "@types/node": "^22.19.3",
55
+ "@vitest/coverage-v8": "^4.0.16",
56
+ "eslint": "^9.39.2",
57
57
  "husky": "^9.1.7",
58
- "jsdom": "^27.2.0",
58
+ "jsdom": "^27.4.0",
59
59
  "typescript": "^5.9.3",
60
- "vite": "^7.2.4",
61
- "vitest": "^4.0.12"
60
+ "vite": "^7.3.0",
61
+ "vitest": "^4.0.16"
62
62
  },
63
63
  "scripts": {
64
64
  "dev": "vite",
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export const VERSION = "2.2.2";
1
+ export const VERSION = "2.2.3";
2
2
 
3
3
  export type { Constructor, Interval, Timeout, ValueOf } from "./core/types.js";
4
4
 
@@ -1,5 +1,3 @@
1
- import AggregatedIterator from "./aggregated-iterator.js";
2
- import AggregatedAsyncIterator from "./aggregated-async-iterator.js";
3
- import ReducedIterator from "./reduced-iterator.js";
4
-
5
- export { AggregatedIterator, AggregatedAsyncIterator, ReducedIterator };
1
+ export { default as AggregatedIterator } from "./aggregated-iterator.js";
2
+ export { default as AggregatedAsyncIterator } from "./aggregated-async-iterator.js";
3
+ export { default as ReducedIterator } from "./reduced-iterator.js";
@@ -79,7 +79,14 @@ export default class CallbackChain<
79
79
  */
80
80
  protected override _invoke(...args: Parameters<T>): ReturnType<T>[]
81
81
  {
82
- return this._callbacks.map((callback) => callback(...args)) as ReturnType<T>[];
82
+ const length = this._callbacks.length;
83
+ const results = new Array<ReturnType<T>>(length);
84
+ for (let i = 0; i < length; i += 1)
85
+ {
86
+ results[i] = this._callbacks[i](...args);
87
+ }
88
+
89
+ return results;
83
90
  }
84
91
 
85
92
  /**
@@ -1,6 +1,4 @@
1
- import CallableObject from "./callable-object.js";
2
- import CallbackChain from "./callback-chain.js";
3
- import Publisher from "./publisher.js";
4
- import SwitchableCallback from "./switchable-callback.js";
5
-
6
- export { CallableObject, CallbackChain, Publisher, SwitchableCallback };
1
+ export { default as CallableObject } from "./callable-object.js";
2
+ export { default as CallbackChain } from "./callback-chain.js";
3
+ export { default as Publisher } from "./publisher.js";
4
+ export { default as SwitchableCallback } from "./switchable-callback.js";
@@ -170,8 +170,14 @@ export default class Publisher<T extends CallbackMap<T> = CallbackMap>
170
170
  let subscribers = this._subscribers.get(event);
171
171
  if (subscribers)
172
172
  {
173
- results = subscribers.slice()
174
- .map((subscriber) => subscriber(...args));
173
+ const _subscribers = subscribers.slice();
174
+ const _length = _subscribers.length;
175
+
176
+ results = new Array<unknown>(_length);
177
+ for (let i = 0; i < _length; i += 1)
178
+ {
179
+ results[i] = _subscribers[i](...args);
180
+ }
175
181
  }
176
182
  else { results = []; }
177
183
 
@@ -1,4 +1,2 @@
1
- import MapView from "./map-view.js";
2
- import SetView from "./set-view.js";
3
-
4
- export { MapView, SetView };
1
+ export { default as MapView } from "./map-view.js";
2
+ export { default as SetView } from "./set-view.js";
@@ -1,4 +1,2 @@
1
- import SmartIterator from "./smart-iterator.js";
2
- import SmartAsyncIterator from "./smart-async-iterator.js";
3
-
4
- export { SmartIterator, SmartAsyncIterator };
1
+ export { default as SmartIterator } from "./smart-iterator.js";
2
+ export { default as SmartAsyncIterator } from "./smart-async-iterator.js";
@@ -1,3 +1 @@
1
- import JSONStorage from "./json-storage.js";
2
-
3
- export { JSONStorage };
1
+ export { default as JSONStorage } from "./json-storage.js";
@@ -1,6 +1,4 @@
1
- import DeferredPromise from "./deferred-promise.js";
2
- import PromiseQueue from "./promise-queue.js";
3
- import SmartPromise from "./smart-promise.js";
4
- import TimedPromise from "./timed-promise.js";
5
-
6
- export { DeferredPromise, PromiseQueue, SmartPromise, TimedPromise };
1
+ export { default as DeferredPromise } from "./deferred-promise.js";
2
+ export { default as PromiseQueue } from "./promise-queue.js";
3
+ export { default as SmartPromise } from "./smart-promise.js";
4
+ export { default as TimedPromise } from "./timed-promise.js";
@@ -1,5 +1,3 @@
1
- import Clock from "./clock.js";
2
- import Countdown from "./countdown.js";
3
- import GameLoop from "./game-loop.js";
4
-
5
- export { Clock, Countdown, GameLoop };
1
+ export { default as Clock } from "./clock.js";
2
+ export { default as Countdown } from "./countdown.js";
3
+ export { default as GameLoop } from "./game-loop.js";
@@ -1,5 +1,5 @@
1
- import Curve from "./curve.js";
2
- import Random from "./random.js";
1
+ export { default as Curve } from "./curve.js";
2
+ export { default as Random } from "./random.js";
3
3
 
4
4
  export { delay, nextAnimationFrame, yieldToEventLoop } from "./async.js";
5
5
  export { dateDifference, dateRange, dateRound, getWeek, TimeUnit, WeekDay } from "./date.js";
@@ -7,5 +7,3 @@ export { loadScript } from "./dom.js";
7
7
  export { chain, count, enumerate, range, shuffle, unique, zip } from "./iterator.js";
8
8
  export { average, hash, sum } from "./math.js";
9
9
  export { capitalize } from "./string.js";
10
-
11
- export { Curve, Random };
@@ -179,6 +179,127 @@ export default class Random
179
179
  return elements[Random.Index(elements)];
180
180
  }
181
181
 
182
+ /**
183
+ * Picks a random sample of elements from a given array without replacement.
184
+ *
185
+ * Uses the Fisher-Yates shuffle algorithm for uniform sampling,
186
+ * which is O(count) instead of O(n log n) for a full shuffle.
187
+ *
188
+ * ---
189
+ *
190
+ * @example
191
+ * ```ts
192
+ * Random.Sample([1, 2, 3, 4, 5], 3); // e.g., [4, 1, 5]
193
+ * ```
194
+ *
195
+ * ---
196
+ *
197
+ * @template T The type of the elements in the array.
198
+ *
199
+ * @param elements
200
+ * The array of elements to sample from.
201
+ *
202
+ * It must contain at least one element. Otherwise, a {@link ValueException} will be thrown.
203
+ *
204
+ * @param count
205
+ * The number of elements to sample.
206
+ *
207
+ * It must be between `0` and `elements.length`. Otherwise, a {@link ValueException} will be thrown.
208
+ *
209
+ * @returns An array containing the randomly sampled elements.
210
+ */
211
+ public static Sample<T>(elements: readonly T[], count: number): T[];
212
+
213
+ /**
214
+ * Picks a weighted random sample of elements from a given array without replacement.
215
+ *
216
+ * Uses the Efraimidis-Spirakis algorithm for weighted sampling.
217
+ * Elements with higher weights have a higher probability of being selected.
218
+ *
219
+ * ---
220
+ *
221
+ * @example
222
+ * ```ts
223
+ * // Element "a" is 3x more likely to be picked than "b" or "c"
224
+ * Random.Sample(["a", "b", "c"], 2, [3, 1, 1]);
225
+ * ```
226
+ *
227
+ * ---
228
+ *
229
+ * @template T The type of the elements in the array.
230
+ *
231
+ * @param elements
232
+ * The array of elements to sample from.
233
+ *
234
+ * It must contain at least one element. Otherwise, a {@link ValueException} will be thrown.
235
+ *
236
+ * @param count
237
+ * The number of elements to sample.
238
+ *
239
+ * It must be between `0` and `elements.length`. Otherwise, a {@link ValueException} will be thrown.
240
+ *
241
+ * @param weights
242
+ * The weights associated with each element.
243
+ *
244
+ * It must have the same length as the elements array.
245
+ * All weights must be greater than zero. Otherwise, a {@link ValueException} will be thrown.
246
+ *
247
+ * @returns An array containing the randomly sampled elements.
248
+ */
249
+ public static Sample<T>(elements: readonly T[], count: number, weights: readonly number[]): T[];
250
+ public static Sample<T>(elements: readonly T[], count: number, weights?: readonly number[]): T[]
251
+ {
252
+ const length = elements.length;
253
+
254
+ if (length === 0) { throw new ValueException("You must provide at least one element."); }
255
+ if (count < 0) { throw new ValueException("Count must be non-negative."); }
256
+ if (count > length) { throw new ValueException("Count cannot exceed the number of elements."); }
257
+
258
+ if (count === 0) { return []; }
259
+
260
+ if (weights === undefined)
261
+ {
262
+ const pool = [...elements];
263
+ const result: T[] = new Array(count);
264
+
265
+ for (let index = 0; index < count; index += 1)
266
+ {
267
+ const randomIndex = this.Integer(index, length);
268
+
269
+ result[index] = pool[randomIndex];
270
+ pool[randomIndex] = pool[index];
271
+ }
272
+
273
+ return result;
274
+ }
275
+
276
+ if (weights.length !== length)
277
+ {
278
+ throw new ValueException("Weights array must have the same length as elements array.");
279
+ }
280
+
281
+ const keys: ({ index: number, key: number })[] = new Array(length);
282
+ for (let index = 0; index < length; index += 1)
283
+ {
284
+ if (weights[index] <= 0)
285
+ {
286
+ throw new ValueException(`Weight for element #${index} must be greater than zero.`);
287
+ }
288
+
289
+ keys[index] = { index: index, key: Math.pow(Math.random(), 1 / weights[index]) };
290
+ }
291
+
292
+ keys.sort((a, b) => b.key - a.key);
293
+
294
+ const result: T[] = new Array(count);
295
+ for (let index = 0; index < count; index += 1)
296
+ {
297
+ result[index] = elements[keys[index].index];
298
+ }
299
+
300
+ return result;
301
+ }
302
+
182
303
  private constructor() { /* ... */ }
183
304
 
184
305
  public readonly [Symbol.toStringTag]: string = "Random";