From bcd4c0efe7a6aee40ff29dddc99f408de92137e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 3 Apr 2022 08:17:24 +0000 Subject: [PATCH 1/4] Bump minimist from 1.2.5 to 1.2.6 Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6. - [Release notes](https://github.com/substack/minimist/releases) - [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6) --- updated-dependencies: - dependency-name: minimist dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7a327a2..58c0b53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -629,9 +629,9 @@ mimic-fn@^2.1.0: integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== neo-async@^2.6.2: version "2.6.2" From 0fc4c470609e4c52f01743d54569d192b8827708 Mon Sep 17 00:00:00 2001 From: HoloPanio Date: Fri, 3 Nov 2023 11:09:53 -0500 Subject: [PATCH 2/4] added to listen for ANY event --- src/index.ts | 66 +- src/lib/ListenerArray.ts | 38 +- src/types/types.ts | 5 +- src/util/Collection.ts | 1310 ++++++++++++++++++++------------------ test.ts | 0 5 files changed, 774 insertions(+), 645 deletions(-) create mode 100644 test.ts diff --git a/src/index.ts b/src/index.ts index 33a80e4..6e0b91f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,12 @@ import ListenerArray from "./lib/ListenerArray"; import { DefaultListener, ListenerSignature } from "./types/events"; +import { AnyListenerCallback } from "./types/types"; -export class Eventra = DefaultListener> { - +export class Eventra< + Events extends ListenerSignature = DefaultListener +> { public constructor() {} - + private _listeners = new ListenerArray({ mode: "recurring" }); private _singularListeners = new ListenerArray({ mode: "once" }); @@ -23,14 +25,14 @@ export class Eventra = DefaultListener> */ emit(event: E, ...args: Parameters) { this._listeners.executeEvent(event, ...args); - this._singularListeners.executeEvent(event, ...args) + this._singularListeners.executeEvent(event, ...args); } /** * Returns an array listing the events for which the emitter has registered listeners. */ eventNames(): E[] { - let finalNamesArray: E[] = [] + let finalNamesArray: E[] = []; this._listeners.storage.map((val, key) => { if (!finalNamesArray.includes(key)) finalNamesArray.push(key); @@ -43,7 +45,6 @@ export class Eventra = DefaultListener> return finalNamesArray; } - /** * Returns the number of listeners listening to the event. */ @@ -51,9 +52,9 @@ export class Eventra = DefaultListener> const recurring = this._listeners.countListeners(eventName); const singular = this._singularListeners.countListeners(eventName); - return (recurring + singular); + return recurring + singular; } - + /** * Returns a copy of the array of listeners for the event. */ @@ -63,14 +64,18 @@ export class Eventra = DefaultListener> return { recurring, - singular - } + singular, + }; } + any(listener: AnyListenerCallback) { + this._listeners.addAny(listener); + return this; + } /** - * Adds the listener function to the end of the listeners array for the event. - * + * Adds the listener function to the end of the listeners array for the event. + * * Returns a reference to the eventra instance, so that calls can be chained. */ on(eventName: E, listener: Events[E]): this { @@ -81,7 +86,7 @@ export class Eventra = DefaultListener> /** * Adds a one-time listener function for the event. * The next time the event is triggered, this listener is removed and then invoked. - * + * * Returns a reference to the eventra instance, so that calls can be chained. */ once(eventName: E, listener: Events[E]): this { @@ -89,38 +94,42 @@ export class Eventra = DefaultListener> return this; } - /** * Adds the listener function to the beginning of the listeners array for the event. - * + * * Returns a reference to the eventra instance, so that calls can be chained. */ - prependListener(eventName: E, listener: Events[E]): this { + prependListener( + eventName: E, + listener: Events[E] + ): this { this._listeners.prepend(eventName, listener); return this; } - + /** * Adds a one-time listener function for the event to the beginning of the listeners array. * The next time the event is triggered, this listener is removed, and then invoked. - * + * * Returns a reference to the eventra instance, so that calls can be chained. */ - prependOnceListener(eventName: E, listener: Events[E]): this { + prependOnceListener( + eventName: E, + listener: Events[E] + ): this { this._singularListeners.prepend(eventName, listener); return this; } - /** * Removes all listeners, or those of the specified event(s). - * + * * Returns a reference to the eventra instance, so that calls can be chained. */ removeAllListeners(...eventName: E[]): this { - const listeners: E[] = [...eventName]; + const listeners: E[] = [...eventName]; - listeners.map(en => { + listeners.map((en) => { this._listeners.removeEvent(en); this._singularListeners.removeEvent(en); }); @@ -130,13 +139,16 @@ export class Eventra = DefaultListener> /** * Removes the specified listener from the listener array for the event. - * + * * Returns a reference to the eventra instance, so that calls can be chained. - */ - removeListener(eventName: E, listener: Events[E]): this { + */ + removeListener( + eventName: E, + listener: Events[E] + ): this { this._listeners.removeListener(eventName, listener); this._singularListeners.removeListener(eventName, listener); return this; } -} \ No newline at end of file +} diff --git a/src/lib/ListenerArray.ts b/src/lib/ListenerArray.ts index 97d8449..426e5e5 100644 --- a/src/lib/ListenerArray.ts +++ b/src/lib/ListenerArray.ts @@ -1,20 +1,29 @@ -import { ListenerArrayMode, ListenerArrayOptions, ListenerCallback } from "../types/types"; +import { + AnyListenerCallback, + ListenerArrayMode, + ListenerArrayOptions, + ListenerCallback, +} from "../types/types"; import Collection from "../util/Collection"; type CollectionType = Collection; export default class ListenerArray { - private _options: ListenerArrayOptions; - private _internalStorage = new Collection() + private _internalStorage = new Collection(); + private _anyListeners: AnyListenerCallback[] = []; constructor(options?: ListenerArrayOptions) { - if (!options) this._options = { mode: "recurring" } + if (!options) this._options = { mode: "recurring" }; else this._options = options; } - get mode(): ListenerArrayMode { return this._options.mode; } - get storage(): CollectionType { return this._internalStorage; } + get mode(): ListenerArrayMode { + return this._options.mode; + } + get storage(): CollectionType { + return this._internalStorage; + } private _updateInternalStorage(collection: CollectionType) { if (!collection) return; @@ -42,6 +51,11 @@ export default class ListenerArray { return this; } + addAny(listener: ListenerCallback): this { + this._anyListeners.push(listener); + return this; + } + prepend(eventName: string | any, listener: ListenerCallback): this { let carbonCopy = this._cloneInternalStorage(); let event = carbonCopy.get(eventName); @@ -63,13 +77,13 @@ export default class ListenerArray { if (!event) return this; if (!event.includes(listener)) return this; - + if (event.length == 1) { carbonCopy.delete(eventName); this._updateInternalStorage(carbonCopy); return this; - }; - + } + const index = event.indexOf(listener); if (index > -1) event.splice(index, 1); @@ -105,6 +119,8 @@ export default class ListenerArray { let carbonCopy = this._cloneInternalStorage(); let event = carbonCopy.get(eventName); + this._anyListeners.map((listener) => listener(eventName, ...args)); + if (!event) return this; event.map((method, index) => { @@ -112,9 +128,9 @@ export default class ListenerArray { return; }); - if (this.mode == 'once') carbonCopy.delete(eventName); + if (this.mode == "once") carbonCopy.delete(eventName); this._updateInternalStorage(carbonCopy); return this; } -} \ No newline at end of file +} diff --git a/src/types/types.ts b/src/types/types.ts index 2f08d0d..4534b34 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -1,6 +1,7 @@ export type ListenerCallback = (...args) => void; +export type AnyListenerCallback = (eventName: string, ...args) => void; export type ListenerArrayMode = "recurring" | "once"; export interface ListenerArrayOptions { - mode: ListenerArrayMode -} \ No newline at end of file + mode: ListenerArrayMode; +} diff --git a/src/util/Collection.ts b/src/util/Collection.ts index d447b82..e248ab1 100644 --- a/src/util/Collection.ts +++ b/src/util/Collection.ts @@ -1,9 +1,9 @@ export interface CollectionConstructor { - new (): Collection; - new (entries?: ReadonlyArray | null): Collection; - new (iterable: Iterable): Collection; - readonly prototype: Collection; - readonly [Symbol.species]: CollectionConstructor; + new (): Collection; + new (entries?: ReadonlyArray | null): Collection; + new (iterable: Iterable): Collection; + readonly prototype: Collection; + readonly [Symbol.species]: CollectionConstructor; } /** @@ -13,607 +13,707 @@ export interface CollectionConstructor { * @property {number} size - The amount of elements in this collection. */ export class Collection extends Map { - public static readonly default: typeof Collection = Collection; - public ['constructor']: CollectionConstructor; - - /** - * Identical to [Map.get()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get). - * Gets an element with the specified key, and returns its value, or `undefined` if the element does not exist. - * @param {*} key - The key to get from this collection - * @returns {* | undefined} - */ - public get(key: K): V | undefined { - return super.get(key); - } - - /** - * Identical to [Map.set()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set). - * Sets a new element in the collection with the specified key and value. - * @param {*} key - The key of the element to add - * @param {*} value - The value of the element to add - * @returns {Collection} - */ - public set(key: K, value: V): this { - return super.set(key, value); - } - - /** - * Identical to [Map.has()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has). - * Checks if an element exists in the collection. - * @param {*} key - The key of the element to check for - * @returns {boolean} `true` if the element exists, `false` if it does not exist. - */ - public has(key: K): boolean { - return super.has(key); - } - - /** - * Identical to [Map.delete()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete). - * Deletes an element from the collection. - * @param {*} key - The key to delete from the collection - * @returns {boolean} `true` if the element was removed, `false` if the element does not exist. - */ - public delete(key: K): boolean { - return super.delete(key); - } - - /** - * Identical to [Map.clear()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear). - * Removes all elements from the collection. - * @returns {undefined} - */ - public clear(): void { - return super.clear(); - } - - /** - * Checks if all of the elements exist in the collection. - * @param {...*} keys - The keys of the elements to check for - * @returns {boolean} `true` if all of the elements exist, `false` if at least one does not exist. - */ - public hasAll(...keys: K[]): boolean { - return keys.every((k) => super.has(k)); - } - - /** - * Checks if any of the elements exist in the collection. - * @param {...*} keys - The keys of the elements to check for - * @returns {boolean} `true` if any of the elements exist, `false` if none exist. - */ - public hasAny(...keys: K[]): boolean { - return keys.some((k) => super.has(k)); - } - - /** - * Obtains the first value(s) in this collection. - * @param {number} [amount] Amount of values to obtain from the beginning - * @returns {*|Array<*>} A single value if no amount is provided or an array of values, starting from the end if - * amount is negative - */ - public first(): V | undefined; - public first(amount: number): V[]; - public first(amount?: number): V | V[] | undefined { - if (typeof amount === 'undefined') return this.values().next().value; - if (amount < 0) return this.last(amount * -1); - amount = Math.min(this.size, amount); - const iter = this.values(); - return Array.from({ length: amount }, (): V => iter.next().value); - } - - /** - * Obtains the first key(s) in this collection. - * @param {number} [amount] Amount of keys to obtain from the beginning - * @returns {*|Array<*>} A single key if no amount is provided or an array of keys, starting from the end if - * amount is negative - */ - public firstKey(): K | undefined; - public firstKey(amount: number): K[]; - public firstKey(amount?: number): K | K[] | undefined { - if (typeof amount === 'undefined') return this.keys().next().value; - if (amount < 0) return this.lastKey(amount * -1); - amount = Math.min(this.size, amount); - const iter = this.keys(); - return Array.from({ length: amount }, (): K => iter.next().value); - } - - /** - * Obtains the last value(s) in this collection. - * @param {number} [amount] Amount of values to obtain from the end - * @returns {*|Array<*>} A single value if no amount is provided or an array of values, starting from the start if - * amount is negative - */ - public last(): V | undefined; - public last(amount: number): V[]; - public last(amount?: number): V | V[] | undefined { - const arr = [...this.values()]; - if (typeof amount === 'undefined') return arr[arr.length - 1]; - if (amount < 0) return this.first(amount * -1); - if (!amount) return []; - return arr.slice(-amount); - } - - /** - * Obtains the last key(s) in this collection. - * @param {number} [amount] Amount of keys to obtain from the end - * @returns {*|Array<*>} A single key if no amount is provided or an array of keys, starting from the start if - * amount is negative - */ - public lastKey(): K | undefined; - public lastKey(amount: number): K[]; - public lastKey(amount?: number): K | K[] | undefined { - const arr = [...this.keys()]; - if (typeof amount === 'undefined') return arr[arr.length - 1]; - if (amount < 0) return this.firstKey(amount * -1); - if (!amount) return []; - return arr.slice(-amount); - } - - /** - * Obtains unique random value(s) from this collection. - * @param {number} [amount] Amount of values to obtain randomly - * @returns {*|Array<*>} A single value if no amount is provided or an array of values - */ - public random(): V; - public random(amount: number): V[]; - public random(amount?: number): V | V[] { - const arr = [...this.values()]; - if (typeof amount === 'undefined') return arr[Math.floor(Math.random() * arr.length)]; - if (!arr.length || !amount) return []; - return Array.from( - { length: Math.min(amount, arr.length) }, - (): V => arr.splice(Math.floor(Math.random() * arr.length), 1)[0], - ); - } - - /** - * Obtains unique random key(s) from this collection. - * @param {number} [amount] Amount of keys to obtain randomly - * @returns {*|Array<*>} A single key if no amount is provided or an array - */ - public randomKey(): K; - public randomKey(amount: number): K[]; - public randomKey(amount?: number): K | K[] { - const arr = [...this.keys()]; - if (typeof amount === 'undefined') return arr[Math.floor(Math.random() * arr.length)]; - if (!arr.length || !amount) return []; - return Array.from( - { length: Math.min(amount, arr.length) }, - (): K => arr.splice(Math.floor(Math.random() * arr.length), 1)[0], - ); - } - - /** - * Searches for a single item where the given function returns a truthy value. This behaves like - * [Array.find()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find). - * All collections used in Discord.js are mapped using their `id` property, and if you want to find by id you - * should use the `get` method. See - * [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get) for details. - * @param {Function} fn The function to test with (should return boolean) - * @param {*} [thisArg] Value to use as `this` when executing function - * @returns {*} - * @example collection.find(user => user.username === 'Bob'); - */ - public find(fn: (value: V, key: K, collection: this) => value is V2): V2 | undefined; - public find(fn: (value: V, key: K, collection: this) => boolean): V | undefined; - public find( - fn: (this: This, value: V, key: K, collection: this) => value is V2, - thisArg: This, - ): V2 | undefined; - public find(fn: (this: This, value: V, key: K, collection: this) => boolean, thisArg: This): V | undefined; - public find(fn: (value: V, key: K, collection: this) => boolean, thisArg?: unknown): V | undefined { - if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg); - for (const [key, val] of this) { - if (fn(val, key, this)) return val; - } - return undefined; - } - - /** - * Searches for the key of a single item where the given function returns a truthy value. This behaves like - * [Array.findIndex()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex), - * but returns the key rather than the positional index. - * @param {Function} fn The function to test with (should return boolean) - * @param {*} [thisArg] Value to use as `this` when executing function - * @returns {*} - * @example collection.findKey(user => user.username === 'Bob'); - */ - public findKey(fn: (value: V, key: K, collection: this) => key is K2): K2 | undefined; - public findKey(fn: (value: V, key: K, collection: this) => boolean): K | undefined; - public findKey( - fn: (this: This, value: V, key: K, collection: this) => key is K2, - thisArg: This, - ): K2 | undefined; - public findKey(fn: (this: This, value: V, key: K, collection: this) => boolean, thisArg: This): K | undefined; - public findKey(fn: (value: V, key: K, collection: this) => boolean, thisArg?: unknown): K | undefined { - if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg); - for (const [key, val] of this) { - if (fn(val, key, this)) return key; - } - return undefined; - } - - /** - * Removes items that satisfy the provided filter function. - * @param {Function} fn Function used to test (should return a boolean) - * @param {*} [thisArg] Value to use as `this` when executing function - * @returns {number} The number of removed entries - */ - public sweep(fn: (value: V, key: K, collection: this) => boolean): number; - public sweep(fn: (this: T, value: V, key: K, collection: this) => boolean, thisArg: T): number; - public sweep(fn: (value: V, key: K, collection: this) => boolean, thisArg?: unknown): number { - if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg); - const previousSize = this.size; - for (const [key, val] of this) { - if (fn(val, key, this)) this.delete(key); - } - return previousSize - this.size; - } - - /** - * Identical to - * [Array.filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter), - * but returns a Collection instead of an Array. - * @param {Function} fn The function to test with (should return boolean) - * @param {*} [thisArg] Value to use as `this` when executing function - * @returns {Collection} - * @example collection.filter(user => user.username === 'Bob'); - */ - public filter(fn: (value: V, key: K, collection: this) => key is K2): Collection; - public filter(fn: (value: V, key: K, collection: this) => value is V2): Collection; - public filter(fn: (value: V, key: K, collection: this) => boolean): Collection; - public filter( - fn: (this: This, value: V, key: K, collection: this) => key is K2, - thisArg: This, - ): Collection; - public filter( - fn: (this: This, value: V, key: K, collection: this) => value is V2, - thisArg: This, - ): Collection; - public filter(fn: (this: This, value: V, key: K, collection: this) => boolean, thisArg: This): Collection; - public filter(fn: (value: V, key: K, collection: this) => boolean, thisArg?: unknown): Collection { - if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg); - const results = new this.constructor[Symbol.species](); - for (const [key, val] of this) { - if (fn(val, key, this)) results.set(key, val); - } - return results; - } - - /** - * Partitions the collection into two collections where the first collection - * contains the items that passed and the second contains the items that failed. - * @param {Function} fn Function used to test (should return a boolean) - * @param {*} [thisArg] Value to use as `this` when executing function - * @returns {Collection[]} - * @example const [big, small] = collection.partition(guild => guild.memberCount > 250); - */ - public partition( - fn: (value: V, key: K, collection: this) => key is K2, - ): [Collection, Collection, V>]; - public partition( - fn: (value: V, key: K, collection: this) => value is V2, - ): [Collection, Collection>]; - public partition(fn: (value: V, key: K, collection: this) => boolean): [Collection, Collection]; - public partition( - fn: (this: This, value: V, key: K, collection: this) => key is K2, - thisArg: This, - ): [Collection, Collection, V>]; - public partition( - fn: (this: This, value: V, key: K, collection: this) => value is V2, - thisArg: This, - ): [Collection, Collection>]; - public partition( - fn: (this: This, value: V, key: K, collection: this) => boolean, - thisArg: This, - ): [Collection, Collection]; - public partition( - fn: (value: V, key: K, collection: this) => boolean, - thisArg?: unknown, - ): [Collection, Collection] { - if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg); - const results: [Collection, Collection] = [ - new this.constructor[Symbol.species](), - new this.constructor[Symbol.species](), - ]; - for (const [key, val] of this) { - if (fn(val, key, this)) { - results[0].set(key, val); - } else { - results[1].set(key, val); - } - } - return results; - } - - /** - * Maps each item into a Collection, then joins the results into a single Collection. Identical in behavior to - * [Array.flatMap()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap). - * @param {Function} fn Function that produces a new Collection - * @param {*} [thisArg] Value to use as `this` when executing function - * @returns {Collection} - * @example collection.flatMap(guild => guild.members.cache); - */ - public flatMap(fn: (value: V, key: K, collection: this) => Collection): Collection; - public flatMap( - fn: (this: This, value: V, key: K, collection: this) => Collection, - thisArg: This, - ): Collection; - public flatMap(fn: (value: V, key: K, collection: this) => Collection, thisArg?: unknown): Collection { - const collections = this.map(fn, thisArg); - return new this.constructor[Symbol.species]().concat(...collections); - } - - /** - * Maps each item to another value into an array. Identical in behavior to - * [Array.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map). - * @param {Function} fn Function that produces an element of the new array, taking three arguments - * @param {*} [thisArg] Value to use as `this` when executing function - * @returns {Array} - * @example collection.map(user => user.tag); - */ - public map(fn: (value: V, key: K, collection: this) => T): T[]; - public map(fn: (this: This, value: V, key: K, collection: this) => T, thisArg: This): T[]; - public map(fn: (value: V, key: K, collection: this) => T, thisArg?: unknown): T[] { - if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg); - const iter = this.entries(); - return Array.from({ length: this.size }, (): T => { - const [key, value] = iter.next().value; - return fn(value, key, this); - }); - } - - /** - * Maps each item to another value into a collection. Identical in behavior to - * [Array.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map). - * @param {Function} fn Function that produces an element of the new collection, taking three arguments - * @param {*} [thisArg] Value to use as `this` when executing function - * @returns {Collection} - * @example collection.mapValues(user => user.tag); - */ - public mapValues(fn: (value: V, key: K, collection: this) => T): Collection; - public mapValues(fn: (this: This, value: V, key: K, collection: this) => T, thisArg: This): Collection; - public mapValues(fn: (value: V, key: K, collection: this) => T, thisArg?: unknown): Collection { - if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg); - const coll = new this.constructor[Symbol.species](); - for (const [key, val] of this) coll.set(key, fn(val, key, this)); - return coll; - } - - /** - * Checks if there exists an item that passes a test. Identical in behavior to - * [Array.some()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some). - * @param {Function} fn Function used to test (should return a boolean) - * @param {*} [thisArg] Value to use as `this` when executing function - * @returns {boolean} - * @example collection.some(user => user.discriminator === '0000'); - */ - public some(fn: (value: V, key: K, collection: this) => boolean): boolean; - public some(fn: (this: T, value: V, key: K, collection: this) => boolean, thisArg: T): boolean; - public some(fn: (value: V, key: K, collection: this) => boolean, thisArg?: unknown): boolean { - if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg); - for (const [key, val] of this) { - if (fn(val, key, this)) return true; - } - return false; - } - - /** - * Checks if all items passes a test. Identical in behavior to - * [Array.every()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every). - * @param {Function} fn Function used to test (should return a boolean) - * @param {*} [thisArg] Value to use as `this` when executing function - * @returns {boolean} - * @example collection.every(user => !user.bot); - */ - public every(fn: (value: V, key: K, collection: this) => key is K2): this is Collection; - public every(fn: (value: V, key: K, collection: this) => value is V2): this is Collection; - public every(fn: (value: V, key: K, collection: this) => boolean): boolean; - public every( - fn: (this: This, value: V, key: K, collection: this) => key is K2, - thisArg: This, - ): this is Collection; - public every( - fn: (this: This, value: V, key: K, collection: this) => value is V2, - thisArg: This, - ): this is Collection; - public every(fn: (this: This, value: V, key: K, collection: this) => boolean, thisArg: This): boolean; - public every(fn: (value: V, key: K, collection: this) => boolean, thisArg?: unknown): boolean { - if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg); - for (const [key, val] of this) { - if (!fn(val, key, this)) return false; - } - return true; - } - - /** - * Applies a function to produce a single value. Identical in behavior to - * [Array.reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce). - * @param {Function} fn Function used to reduce, taking four arguments; `accumulator`, `currentValue`, `currentKey`, - * and `collection` - * @param {*} [initialValue] Starting value for the accumulator - * @returns {*} - * @example collection.reduce((acc, guild) => acc + guild.memberCount, 0); - */ - public reduce(fn: (accumulator: T, value: V, key: K, collection: this) => T, initialValue?: T): T { - let accumulator!: T; - - if (typeof initialValue !== 'undefined') { - accumulator = initialValue; - for (const [key, val] of this) accumulator = fn(accumulator, val, key, this); - return accumulator; - } - let first = true; - for (const [key, val] of this) { - if (first) { - accumulator = val as unknown as T; - first = false; - continue; - } - accumulator = fn(accumulator, val, key, this); - } - - // No items iterated. - if (first) { - throw new TypeError('Reduce of empty collection with no initial value'); - } - - return accumulator; - } - - /** - * Identical to - * [Map.forEach()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach), - * but returns the collection instead of undefined. - * @param {Function} fn Function to execute for each element - * @param {*} [thisArg] Value to use as `this` when executing function - * @returns {Collection} - * @example - * collection - * .each(user => console.log(user.username)) - * .filter(user => user.bot) - * .each(user => console.log(user.username)); - */ - public each(fn: (value: V, key: K, collection: this) => void): this; - public each(fn: (this: T, value: V, key: K, collection: this) => void, thisArg: T): this; - public each(fn: (value: V, key: K, collection: this) => void, thisArg?: unknown): this { - this.forEach(fn as (value: V, key: K, map: Map) => void, thisArg); - return this; - } - - /** - * Runs a function on the collection and returns the collection. - * @param {Function} fn Function to execute - * @param {*} [thisArg] Value to use as `this` when executing function - * @returns {Collection} - * @example - * collection - * .tap(coll => console.log(coll.size)) - * .filter(user => user.bot) - * .tap(coll => console.log(coll.size)) - */ - public tap(fn: (collection: this) => void): this; - public tap(fn: (this: T, collection: this) => void, thisArg: T): this; - public tap(fn: (collection: this) => void, thisArg?: unknown): this { - if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg); - fn(this); - return this; - } - - /** - * Creates an identical shallow copy of this collection. - * @returns {Collection} - * @example const newColl = someColl.clone(); - */ - public clone(): Collection { - return new this.constructor[Symbol.species](this); - } - - /** - * Combines this collection with others into a new collection. None of the source collections are modified. - * @param {...Collection} collections Collections to merge - * @returns {Collection} - * @example const newColl = someColl.concat(someOtherColl, anotherColl, ohBoyAColl); - */ - public concat(...collections: Collection[]): Collection { - const newColl = this.clone(); - for (const coll of collections) { - for (const [key, val] of coll) newColl.set(key, val); - } - return newColl; - } - - /** - * Checks if this collection shares identical items with another. - * This is different to checking for equality using equal-signs, because - * the collections may be different objects, but contain the same data. - * @param {Collection} collection Collection to compare with - * @returns {boolean} Whether the collections have identical contents - */ - public equals(collection: Collection): boolean { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (!collection) return false; // runtime check - if (this === collection) return true; - if (this.size !== collection.size) return false; - for (const [key, value] of this) { - if (!collection.has(key) || value !== collection.get(key)) { - return false; - } - } - return true; - } - - /** - * The sort method sorts the items of a collection in place and returns it. - * The sort is not necessarily stable in Node 10 or older. - * The default sort order is according to string Unicode code points. - * @param {Function} [compareFunction] Specifies a function that defines the sort order. - * If omitted, the collection is sorted according to each character's Unicode code point value, - * according to the string conversion of each element. - * @returns {Collection} - * @example collection.sort((userA, userB) => userA.createdTimestamp - userB.createdTimestamp); - */ - public sort(compareFunction: Comparator = Collection.defaultSort): this { - const entries = [...this.entries()]; - entries.sort((a, b): number => compareFunction(a[1], b[1], a[0], b[0])); - - // Perform clean-up - super.clear(); - - // Set the new entries - for (const [k, v] of entries) { - super.set(k, v); - } - return this; - } - - /** - * The intersect method returns a new structure containing items where the keys are present in both original structures. - * @param {Collection} other The other Collection to filter against - * @returns {Collection} - */ - public intersect(other: Collection): Collection { - const coll = new this.constructor[Symbol.species](); - for (const [k, v] of other) { - if (this.has(k)) coll.set(k, v); - } - return coll; - } - - /** - * The difference method returns a new structure containing items where the key is present in one of the original structures but not the other. - * @param {Collection} other The other Collection to filter against - * @returns {Collection} - */ - public difference(other: Collection): Collection { - const coll = new this.constructor[Symbol.species](); - for (const [k, v] of other) { - if (!this.has(k)) coll.set(k, v); - } - for (const [k, v] of this) { - if (!other.has(k)) coll.set(k, v); - } - return coll; - } - - /** - * The sorted method sorts the items of a collection and returns it. - * The sort is not necessarily stable in Node 10 or older. - * The default sort order is according to string Unicode code points. - * @param {Function} [compareFunction] Specifies a function that defines the sort order. - * If omitted, the collection is sorted according to each character's Unicode code point value, - * according to the string conversion of each element. - * @returns {Collection} - * @example collection.sorted((userA, userB) => userA.createdTimestamp - userB.createdTimestamp); - */ - public sorted(compareFunction: Comparator = Collection.defaultSort): Collection { - return new this.constructor[Symbol.species](this).sort((av, bv, ak, bk) => compareFunction(av, bv, ak, bk)); - } - - private static defaultSort(firstValue: V, secondValue: V): number { - return Number(firstValue > secondValue) || Number(firstValue === secondValue) - 1; - } + public static readonly default: typeof Collection = Collection; + // public ['constructor']: CollectionConstructor; + + /** + * Identical to [Map.get()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get). + * Gets an element with the specified key, and returns its value, or `undefined` if the element does not exist. + * @param {*} key - The key to get from this collection + * @returns {* | undefined} + */ + public get(key: K): V | undefined { + return super.get(key); + } + + /** + * Identical to [Map.set()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set). + * Sets a new element in the collection with the specified key and value. + * @param {*} key - The key of the element to add + * @param {*} value - The value of the element to add + * @returns {Collection} + */ + public set(key: K, value: V): this { + return super.set(key, value); + } + + /** + * Identical to [Map.has()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has). + * Checks if an element exists in the collection. + * @param {*} key - The key of the element to check for + * @returns {boolean} `true` if the element exists, `false` if it does not exist. + */ + public has(key: K): boolean { + return super.has(key); + } + + /** + * Identical to [Map.delete()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete). + * Deletes an element from the collection. + * @param {*} key - The key to delete from the collection + * @returns {boolean} `true` if the element was removed, `false` if the element does not exist. + */ + public delete(key: K): boolean { + return super.delete(key); + } + + /** + * Identical to [Map.clear()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear). + * Removes all elements from the collection. + * @returns {undefined} + */ + public clear(): void { + return super.clear(); + } + + /** + * Checks if all of the elements exist in the collection. + * @param {...*} keys - The keys of the elements to check for + * @returns {boolean} `true` if all of the elements exist, `false` if at least one does not exist. + */ + public hasAll(...keys: K[]): boolean { + return keys.every((k) => super.has(k)); + } + + /** + * Checks if any of the elements exist in the collection. + * @param {...*} keys - The keys of the elements to check for + * @returns {boolean} `true` if any of the elements exist, `false` if none exist. + */ + public hasAny(...keys: K[]): boolean { + return keys.some((k) => super.has(k)); + } + + /** + * Obtains the first value(s) in this collection. + * @param {number} [amount] Amount of values to obtain from the beginning + * @returns {*|Array<*>} A single value if no amount is provided or an array of values, starting from the end if + * amount is negative + */ + public first(): V | undefined; + public first(amount: number): V[]; + public first(amount?: number): V | V[] | undefined { + if (typeof amount === "undefined") return this.values().next().value; + if (amount < 0) return this.last(amount * -1); + amount = Math.min(this.size, amount); + const iter = this.values(); + return Array.from({ length: amount }, (): V => iter.next().value); + } + + /** + * Obtains the first key(s) in this collection. + * @param {number} [amount] Amount of keys to obtain from the beginning + * @returns {*|Array<*>} A single key if no amount is provided or an array of keys, starting from the end if + * amount is negative + */ + public firstKey(): K | undefined; + public firstKey(amount: number): K[]; + public firstKey(amount?: number): K | K[] | undefined { + if (typeof amount === "undefined") return this.keys().next().value; + if (amount < 0) return this.lastKey(amount * -1); + amount = Math.min(this.size, amount); + const iter = this.keys(); + return Array.from({ length: amount }, (): K => iter.next().value); + } + + /** + * Obtains the last value(s) in this collection. + * @param {number} [amount] Amount of values to obtain from the end + * @returns {*|Array<*>} A single value if no amount is provided or an array of values, starting from the start if + * amount is negative + */ + public last(): V | undefined; + public last(amount: number): V[]; + public last(amount?: number): V | V[] | undefined { + const arr = [...this.values()]; + if (typeof amount === "undefined") return arr[arr.length - 1]; + if (amount < 0) return this.first(amount * -1); + if (!amount) return []; + return arr.slice(-amount); + } + + /** + * Obtains the last key(s) in this collection. + * @param {number} [amount] Amount of keys to obtain from the end + * @returns {*|Array<*>} A single key if no amount is provided or an array of keys, starting from the start if + * amount is negative + */ + public lastKey(): K | undefined; + public lastKey(amount: number): K[]; + public lastKey(amount?: number): K | K[] | undefined { + const arr = [...this.keys()]; + if (typeof amount === "undefined") return arr[arr.length - 1]; + if (amount < 0) return this.firstKey(amount * -1); + if (!amount) return []; + return arr.slice(-amount); + } + + /** + * Obtains unique random value(s) from this collection. + * @param {number} [amount] Amount of values to obtain randomly + * @returns {*|Array<*>} A single value if no amount is provided or an array of values + */ + public random(): V; + public random(amount: number): V[]; + public random(amount?: number): V | V[] { + const arr = [...this.values()]; + if (typeof amount === "undefined") + return arr[Math.floor(Math.random() * arr.length)]; + if (!arr.length || !amount) return []; + return Array.from( + { length: Math.min(amount, arr.length) }, + (): V => arr.splice(Math.floor(Math.random() * arr.length), 1)[0] + ); + } + + /** + * Obtains unique random key(s) from this collection. + * @param {number} [amount] Amount of keys to obtain randomly + * @returns {*|Array<*>} A single key if no amount is provided or an array + */ + public randomKey(): K; + public randomKey(amount: number): K[]; + public randomKey(amount?: number): K | K[] { + const arr = [...this.keys()]; + if (typeof amount === "undefined") + return arr[Math.floor(Math.random() * arr.length)]; + if (!arr.length || !amount) return []; + return Array.from( + { length: Math.min(amount, arr.length) }, + (): K => arr.splice(Math.floor(Math.random() * arr.length), 1)[0] + ); + } + + /** + * Searches for a single item where the given function returns a truthy value. This behaves like + * [Array.find()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find). + * All collections used in Discord.js are mapped using their `id` property, and if you want to find by id you + * should use the `get` method. See + * [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get) for details. + * @param {Function} fn The function to test with (should return boolean) + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {*} + * @example collection.find(user => user.username === 'Bob'); + */ + public find( + fn: (value: V, key: K, collection: this) => value is V2 + ): V2 | undefined; + public find( + fn: (value: V, key: K, collection: this) => boolean + ): V | undefined; + public find( + fn: (this: This, value: V, key: K, collection: this) => value is V2, + thisArg: This + ): V2 | undefined; + public find( + fn: (this: This, value: V, key: K, collection: this) => boolean, + thisArg: This + ): V | undefined; + public find( + fn: (value: V, key: K, collection: this) => boolean, + thisArg?: unknown + ): V | undefined { + if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); + for (const [key, val] of this) { + if (fn(val, key, this)) return val; + } + return undefined; + } + + /** + * Searches for the key of a single item where the given function returns a truthy value. This behaves like + * [Array.findIndex()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex), + * but returns the key rather than the positional index. + * @param {Function} fn The function to test with (should return boolean) + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {*} + * @example collection.findKey(user => user.username === 'Bob'); + */ + public findKey( + fn: (value: V, key: K, collection: this) => key is K2 + ): K2 | undefined; + public findKey( + fn: (value: V, key: K, collection: this) => boolean + ): K | undefined; + public findKey( + fn: (this: This, value: V, key: K, collection: this) => key is K2, + thisArg: This + ): K2 | undefined; + public findKey( + fn: (this: This, value: V, key: K, collection: this) => boolean, + thisArg: This + ): K | undefined; + public findKey( + fn: (value: V, key: K, collection: this) => boolean, + thisArg?: unknown + ): K | undefined { + if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); + for (const [key, val] of this) { + if (fn(val, key, this)) return key; + } + return undefined; + } + + /** + * Removes items that satisfy the provided filter function. + * @param {Function} fn Function used to test (should return a boolean) + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {number} The number of removed entries + */ + public sweep(fn: (value: V, key: K, collection: this) => boolean): number; + public sweep( + fn: (this: T, value: V, key: K, collection: this) => boolean, + thisArg: T + ): number; + public sweep( + fn: (value: V, key: K, collection: this) => boolean, + thisArg?: unknown + ): number { + if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); + const previousSize = this.size; + for (const [key, val] of this) { + if (fn(val, key, this)) this.delete(key); + } + return previousSize - this.size; + } + + /** + * Identical to + * [Array.filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter), + * but returns a Collection instead of an Array. + * @param {Function} fn The function to test with (should return boolean) + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {Collection} + * @example collection.filter(user => user.username === 'Bob'); + */ + public filter( + fn: (value: V, key: K, collection: this) => key is K2 + ): Collection; + public filter( + fn: (value: V, key: K, collection: this) => value is V2 + ): Collection; + public filter( + fn: (value: V, key: K, collection: this) => boolean + ): Collection; + public filter( + fn: (this: This, value: V, key: K, collection: this) => key is K2, + thisArg: This + ): Collection; + public filter( + fn: (this: This, value: V, key: K, collection: this) => value is V2, + thisArg: This + ): Collection; + public filter( + fn: (this: This, value: V, key: K, collection: this) => boolean, + thisArg: This + ): Collection; + public filter( + fn: (value: V, key: K, collection: this) => boolean, + thisArg?: unknown + ): Collection { + if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); + const results = new this.constructor[Symbol.species](); + for (const [key, val] of this) { + if (fn(val, key, this)) results.set(key, val); + } + return results; + } + + /** + * Partitions the collection into two collections where the first collection + * contains the items that passed and the second contains the items that failed. + * @param {Function} fn Function used to test (should return a boolean) + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {Collection[]} + * @example const [big, small] = collection.partition(guild => guild.memberCount > 250); + */ + public partition( + fn: (value: V, key: K, collection: this) => key is K2 + ): [Collection, Collection, V>]; + public partition( + fn: (value: V, key: K, collection: this) => value is V2 + ): [Collection, Collection>]; + public partition( + fn: (value: V, key: K, collection: this) => boolean + ): [Collection, Collection]; + public partition( + fn: (this: This, value: V, key: K, collection: this) => key is K2, + thisArg: This + ): [Collection, Collection, V>]; + public partition( + fn: (this: This, value: V, key: K, collection: this) => value is V2, + thisArg: This + ): [Collection, Collection>]; + public partition( + fn: (this: This, value: V, key: K, collection: this) => boolean, + thisArg: This + ): [Collection, Collection]; + public partition( + fn: (value: V, key: K, collection: this) => boolean, + thisArg?: unknown + ): [Collection, Collection] { + if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); + const results: [Collection, Collection] = [ + new this.constructor[Symbol.species](), + new this.constructor[Symbol.species](), + ]; + for (const [key, val] of this) { + if (fn(val, key, this)) { + results[0].set(key, val); + } else { + results[1].set(key, val); + } + } + return results; + } + + /** + * Maps each item into a Collection, then joins the results into a single Collection. Identical in behavior to + * [Array.flatMap()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap). + * @param {Function} fn Function that produces a new Collection + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {Collection} + * @example collection.flatMap(guild => guild.members.cache); + */ + public flatMap( + fn: (value: V, key: K, collection: this) => Collection + ): Collection; + public flatMap( + fn: (this: This, value: V, key: K, collection: this) => Collection, + thisArg: This + ): Collection; + public flatMap( + fn: (value: V, key: K, collection: this) => Collection, + thisArg?: unknown + ): Collection { + const collections = this.map(fn, thisArg); + return new this.constructor[Symbol.species]().concat(...collections); + } + + /** + * Maps each item to another value into an array. Identical in behavior to + * [Array.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map). + * @param {Function} fn Function that produces an element of the new array, taking three arguments + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {Array} + * @example collection.map(user => user.tag); + */ + public map(fn: (value: V, key: K, collection: this) => T): T[]; + public map( + fn: (this: This, value: V, key: K, collection: this) => T, + thisArg: This + ): T[]; + public map( + fn: (value: V, key: K, collection: this) => T, + thisArg?: unknown + ): T[] { + if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); + const iter = this.entries(); + return Array.from({ length: this.size }, (): T => { + const [key, value] = iter.next().value; + return fn(value, key, this); + }); + } + + /** + * Maps each item to another value into a collection. Identical in behavior to + * [Array.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map). + * @param {Function} fn Function that produces an element of the new collection, taking three arguments + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {Collection} + * @example collection.mapValues(user => user.tag); + */ + public mapValues( + fn: (value: V, key: K, collection: this) => T + ): Collection; + public mapValues( + fn: (this: This, value: V, key: K, collection: this) => T, + thisArg: This + ): Collection; + public mapValues( + fn: (value: V, key: K, collection: this) => T, + thisArg?: unknown + ): Collection { + if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); + const coll = new this.constructor[Symbol.species](); + for (const [key, val] of this) coll.set(key, fn(val, key, this)); + return coll; + } + + /** + * Checks if there exists an item that passes a test. Identical in behavior to + * [Array.some()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some). + * @param {Function} fn Function used to test (should return a boolean) + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {boolean} + * @example collection.some(user => user.discriminator === '0000'); + */ + public some(fn: (value: V, key: K, collection: this) => boolean): boolean; + public some( + fn: (this: T, value: V, key: K, collection: this) => boolean, + thisArg: T + ): boolean; + public some( + fn: (value: V, key: K, collection: this) => boolean, + thisArg?: unknown + ): boolean { + if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); + for (const [key, val] of this) { + if (fn(val, key, this)) return true; + } + return false; + } + + /** + * Checks if all items passes a test. Identical in behavior to + * [Array.every()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every). + * @param {Function} fn Function used to test (should return a boolean) + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {boolean} + * @example collection.every(user => !user.bot); + */ + public every( + fn: (value: V, key: K, collection: this) => key is K2 + ): this is Collection; + public every( + fn: (value: V, key: K, collection: this) => value is V2 + ): this is Collection; + public every(fn: (value: V, key: K, collection: this) => boolean): boolean; + public every( + fn: (this: This, value: V, key: K, collection: this) => key is K2, + thisArg: This + ): this is Collection; + public every( + fn: (this: This, value: V, key: K, collection: this) => value is V2, + thisArg: This + ): this is Collection; + public every( + fn: (this: This, value: V, key: K, collection: this) => boolean, + thisArg: This + ): boolean; + public every( + fn: (value: V, key: K, collection: this) => boolean, + thisArg?: unknown + ): boolean { + if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); + for (const [key, val] of this) { + if (!fn(val, key, this)) return false; + } + return true; + } + + /** + * Applies a function to produce a single value. Identical in behavior to + * [Array.reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce). + * @param {Function} fn Function used to reduce, taking four arguments; `accumulator`, `currentValue`, `currentKey`, + * and `collection` + * @param {*} [initialValue] Starting value for the accumulator + * @returns {*} + * @example collection.reduce((acc, guild) => acc + guild.memberCount, 0); + */ + public reduce( + fn: (accumulator: T, value: V, key: K, collection: this) => T, + initialValue?: T + ): T { + let accumulator!: T; + + if (typeof initialValue !== "undefined") { + accumulator = initialValue; + for (const [key, val] of this) + accumulator = fn(accumulator, val, key, this); + return accumulator; + } + let first = true; + for (const [key, val] of this) { + if (first) { + accumulator = val as unknown as T; + first = false; + continue; + } + accumulator = fn(accumulator, val, key, this); + } + + // No items iterated. + if (first) { + throw new TypeError("Reduce of empty collection with no initial value"); + } + + return accumulator; + } + + /** + * Identical to + * [Map.forEach()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach), + * but returns the collection instead of undefined. + * @param {Function} fn Function to execute for each element + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {Collection} + * @example + * collection + * .each(user => console.log(user.username)) + * .filter(user => user.bot) + * .each(user => console.log(user.username)); + */ + public each(fn: (value: V, key: K, collection: this) => void): this; + public each( + fn: (this: T, value: V, key: K, collection: this) => void, + thisArg: T + ): this; + public each( + fn: (value: V, key: K, collection: this) => void, + thisArg?: unknown + ): this { + this.forEach(fn as (value: V, key: K, map: Map) => void, thisArg); + return this; + } + + /** + * Runs a function on the collection and returns the collection. + * @param {Function} fn Function to execute + * @param {*} [thisArg] Value to use as `this` when executing function + * @returns {Collection} + * @example + * collection + * .tap(coll => console.log(coll.size)) + * .filter(user => user.bot) + * .tap(coll => console.log(coll.size)) + */ + public tap(fn: (collection: this) => void): this; + public tap(fn: (this: T, collection: this) => void, thisArg: T): this; + public tap(fn: (collection: this) => void, thisArg?: unknown): this { + if (typeof thisArg !== "undefined") fn = fn.bind(thisArg); + fn(this); + return this; + } + + /** + * Creates an identical shallow copy of this collection. + * @returns {Collection} + * @example const newColl = someColl.clone(); + */ + public clone(): Collection { + return new this.constructor[Symbol.species](this); + } + + /** + * Combines this collection with others into a new collection. None of the source collections are modified. + * @param {...Collection} collections Collections to merge + * @returns {Collection} + * @example const newColl = someColl.concat(someOtherColl, anotherColl, ohBoyAColl); + */ + public concat(...collections: Collection[]): Collection { + const newColl = this.clone(); + for (const coll of collections) { + for (const [key, val] of coll) newColl.set(key, val); + } + return newColl; + } + + /** + * Checks if this collection shares identical items with another. + * This is different to checking for equality using equal-signs, because + * the collections may be different objects, but contain the same data. + * @param {Collection} collection Collection to compare with + * @returns {boolean} Whether the collections have identical contents + */ + public equals(collection: Collection): boolean { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (!collection) return false; // runtime check + if (this === collection) return true; + if (this.size !== collection.size) return false; + for (const [key, value] of this) { + if (!collection.has(key) || value !== collection.get(key)) { + return false; + } + } + return true; + } + + /** + * The sort method sorts the items of a collection in place and returns it. + * The sort is not necessarily stable in Node 10 or older. + * The default sort order is according to string Unicode code points. + * @param {Function} [compareFunction] Specifies a function that defines the sort order. + * If omitted, the collection is sorted according to each character's Unicode code point value, + * according to the string conversion of each element. + * @returns {Collection} + * @example collection.sort((userA, userB) => userA.createdTimestamp - userB.createdTimestamp); + */ + public sort( + compareFunction: Comparator = Collection.defaultSort + ): this { + const entries = [...this.entries()]; + entries.sort((a, b): number => compareFunction(a[1], b[1], a[0], b[0])); + + // Perform clean-up + super.clear(); + + // Set the new entries + for (const [k, v] of entries) { + super.set(k, v); + } + return this; + } + + /** + * The intersect method returns a new structure containing items where the keys are present in both original structures. + * @param {Collection} other The other Collection to filter against + * @returns {Collection} + */ + public intersect(other: Collection): Collection { + const coll = new this.constructor[Symbol.species](); + for (const [k, v] of other) { + if (this.has(k)) coll.set(k, v); + } + return coll; + } + + /** + * The difference method returns a new structure containing items where the key is present in one of the original structures but not the other. + * @param {Collection} other The other Collection to filter against + * @returns {Collection} + */ + public difference(other: Collection): Collection { + const coll = new this.constructor[Symbol.species](); + for (const [k, v] of other) { + if (!this.has(k)) coll.set(k, v); + } + for (const [k, v] of this) { + if (!other.has(k)) coll.set(k, v); + } + return coll; + } + + /** + * The sorted method sorts the items of a collection and returns it. + * The sort is not necessarily stable in Node 10 or older. + * The default sort order is according to string Unicode code points. + * @param {Function} [compareFunction] Specifies a function that defines the sort order. + * If omitted, the collection is sorted according to each character's Unicode code point value, + * according to the string conversion of each element. + * @returns {Collection} + * @example collection.sorted((userA, userB) => userA.createdTimestamp - userB.createdTimestamp); + */ + public sorted( + compareFunction: Comparator = Collection.defaultSort + ): Collection { + return new this.constructor[Symbol.species](this).sort((av, bv, ak, bk) => + compareFunction(av, bv, ak, bk) + ); + } + + private static defaultSort(firstValue: V, secondValue: V): number { + return ( + Number(firstValue > secondValue) || Number(firstValue === secondValue) - 1 + ); + } } -export type Comparator = (firstValue: V, secondValue: V, firstKey: K, secondKey: K) => number; +export type Comparator = ( + firstValue: V, + secondValue: V, + firstKey: K, + secondKey: K +) => number; export default Collection; diff --git a/test.ts b/test.ts new file mode 100644 index 0000000..e69de29 From 95f38d6255155cc1f4f110def6819437aa905a2c Mon Sep 17 00:00:00 2001 From: HoloPanio Date: Fri, 3 Nov 2023 11:13:56 -0500 Subject: [PATCH 3/4] Added comment to main file --- src/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 6e0b91f..a0ad360 100644 --- a/src/index.ts +++ b/src/index.ts @@ -67,7 +67,9 @@ export class Eventra< singular, }; } - + /** + * Adds a listener that will callback after every single event execution. + */ any(listener: AnyListenerCallback) { this._listeners.addAny(listener); return this; From 14696a1c97ff6c86d99d8b98a43e4e77b9e26b77 Mon Sep 17 00:00:00 2001 From: HoloPanio Date: Fri, 3 Nov 2023 11:15:24 -0500 Subject: [PATCH 4/4] remove test file --- test.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test.ts diff --git a/test.ts b/test.ts deleted file mode 100644 index e69de29..0000000