src/structures/Collection.js
/**
* A Map with additional utility methods. This is used throughout discord.js rather than Arrays for anything that has
* an ID, for significantly improved performance and ease-of-use.
* @extends {Map}
*/
class Collection extends Map {
constructor(iterable) {
super(iterable);
/**
* Cached array for the `array()` method - will be reset to `null` whenever `set()` or `delete()` are called
* @name Collection#_array
* @type {?Array}
* @private
*/
Object.defineProperty(this, '_array', { value: null, writable: true, configurable: true });
/**
* Cached array for the `keyArray()` method - will be reset to `null` whenever `set()` or `delete()` are called
* @name Collection#_keyArray
* @type {?Array}
* @private
*/
Object.defineProperty(this, '_keyArray', { value: null, writable: true, configurable: true });
}
set(key, val) {
this._array = null;
this._keyArray = null;
return super.set(key, val);
}
delete(key) {
this._array = null;
this._keyArray = null;
return super.delete(key);
}
/**
* Creates an ordered array of the values of this collection, and caches it internally. The array will only be
* reconstructed if an item is added to or removed from the collection, or if you change the length of the array
* itself. If you don't want this caching behaviour, use `Array.from(collection.values())` instead.
* @returns {Array}
*/
array() {
if (!this._array || this._array.length !== this.size) this._array = Array.from(this.values());
return this._array;
}
/**
* Searches for a single item where its specified property's value is identical to the given value
* (`item[prop] === value`), or the given function returns a truthy value. In the latter case, this is identical to
* [Array.find()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find).
* <warn>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.</warn>
* @param {string|Function} propOrFn The property to test against, or the function to test with
* @param {*} [value] The expected value - only applicable and required if using a property for the first argument
* @returns {*}
* @example
* collection.find('username', 'Bob');
* @example
* collection.find(val => val.username === 'Bob');
*/
find(propOrFn, value) {
if (typeof propOrFn === 'string') {
if (typeof value === 'undefined') throw new Error('Value must be specified.');
for (const item of this.values()) {
if (item[propOrFn] === value) return item;
}
return null;
} else if (typeof propOrFn === 'function') {
for (const [key, val] of this) {
if (propOrFn(val, key, this)) return val;
}
return null;
} else {
throw new Error('First argument must be a property string or a function.');
}
}
}
module.exports = Collection;