From 06f4c679d39adc1b36b65dcc9663e930ce56bb64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89velyne=20Lachance?= Date: Wed, 7 Jun 2017 18:52:41 -0400 Subject: [PATCH] Add `count` optional argument to Collection methods (#1552) * Add `count` optional argument to Collection methods [NON-BREAKING CHANGE] An optional `count` argument is added to the following methods: - random() and randomKey() - first() and firstKey() - last() and lastKey() If `count` is used, the method returns an array instead of only the value. Performance impact non-existent for existing code. Performance for returning an array has been measured and this is the fastest I could find (array[i] = value is faster than array.push()). * Update Collection.js Fixed spacing/line length errors according to suggestions by codacy/pr * Fixed docs Added proper `@returns {*|Array}` as the methods might return either. Also added params where missing (whoops) * Further doc fixes Per Crawl's comments, fixed (i + 1) spacing as well as fixed {Integer} to {number} * random() and randomKey() fix Per Hydra's comment, random() and randomKey() now ensures unique values. I've also resolved potential issues with requesting a count higher than the collection size. A collection with 10 items will only ever return at most 10 items using the `count` property. * Can I facepalm harder Had wrong header comments ^_^ * Fixed for "values/value" and Omited Also, added "Positive" integer check. * looks like I "omitted" a change, there. * Update Collection.js * Update Collection.js * Update Collection.js --- src/util/Collection.js | 100 ++++++++++++++++++++++++++++------------- 1 file changed, 70 insertions(+), 30 deletions(-) diff --git a/src/util/Collection.js b/src/util/Collection.js index 620d4565d..b95af8562 100644 --- a/src/util/Collection.js +++ b/src/util/Collection.js @@ -59,59 +59,99 @@ class Collection extends Map { } /** - * Obtains the first item in this collection. - * @returns {*} + * Obtains the first value(s) in this collection. + * @param {number} [count] Number of values to obtain from the beginning + * @returns {*|Array<*>} The single value if `count` is undefined, or an array of values of `count` length */ - first() { - return this.values().next().value; + first(count) { + if (count === undefined) return this.values().next().value; + if (typeof count !== 'number') throw new TypeError('The count must be a number.'); + if (!Number.isInteger(count) || count < 1) throw new RangeError('The count must be an integer greater than 0.'); + count = Math.min(this.size, count); + const arr = new Array(count); + const iter = this.values(); + for (let i = 0; i < count; i++) arr[i] = iter.next().value; + return arr; } /** - * Obtains the first key in this collection. - * @returns {*} + * Obtains the first key(s) in this collection. + * @param {number} [count] Number of keys to obtain from the beginning + * @returns {*|Array<*>} The single key if `count` is undefined, or an array of keys of `count` length */ - firstKey() { - return this.keys().next().value; + firstKey(count) { + if (count === undefined) return this.keys().next().value; + if (typeof count !== 'number') throw new TypeError('The count must be a number.'); + if (!Number.isInteger(count) || count < 1) throw new RangeError('The count must be an integer greater than 0.'); + count = Math.min(this.size, count); + const arr = new Array(count); + const iter = this.iter(); + for (let i = 0; i < count; i++) arr[i] = iter.next().value; + return arr; } /** - * Obtains the last item in this collection. This relies on the `array()` method, and thus the caching mechanism - * applies here as well. - * @returns {*} + * Obtains the last value(s) in this collection. This relies on {@link Collection#array}, and thus the caching + * mechanism applies here as well. + * @param {number} [count] Number of values to obtain from the end + * @returns {*|Array<*>} The single value if `count` is undefined, or an array of values of `count` length */ - last() { + last(count) { const arr = this.array(); - return arr[arr.length - 1]; + if (count === undefined) return arr[arr.length - 1]; + if (typeof count !== 'number') throw new TypeError('The count must be a number.'); + if (!Number.isInteger(count) || count < 1) throw new RangeError('The count must be an integer greater than 0.'); + return arr.slice(-count); } /** - * Obtains the last key in this collection. This relies on the `keyArray()` method, and thus the caching mechanism - * applies here as well. - * @returns {*} + * Obtains the last key(s) in this collection. This relies on {@link Collection#keyArray}, and thus the caching + * mechanism applies here as well. + * @param {number} [count] Number of keys to obtain from the end + * @returns {*|Array<*>} The single key if `count` is undefined, or an array of keys of `count` length */ - lastKey() { + lastKey(count) { const arr = this.keyArray(); - return arr[arr.length - 1]; + if (count === undefined) return arr[arr.length - 1]; + if (typeof count !== 'number') throw new TypeError('The count must be a number.'); + if (!Number.isInteger(count) || count < 1) throw new RangeError('The count must be an integer greater than 0.'); + return arr.slice(-count); } /** - * Obtains a random item from this collection. This relies on the `array()` method, and thus the caching mechanism - * applies here as well. - * @returns {*} + * Obtains random value(s) from this collection. This relies on {@link Collection#array}, and thus the caching + * mechanism applies here as well. + * @param {number} [count] Number of values to obtain randomly + * @returns {*|Array<*>} The single value if `count` is undefined, or an array of values of `count` length */ - random() { - const arr = this.array(); - return arr[Math.floor(Math.random() * arr.length)]; + random(count) { + let arr = this.array(); + if (count === undefined) return arr[Math.floor(Math.random() * arr.length)]; + if (typeof count !== 'number') throw new TypeError('The count must be a number.'); + if (!Number.isInteger(count) || count < 1) throw new RangeError('The count must be an integer greater than 0.'); + if (arr.length === 0) return []; + const rand = new Array(count); + arr = arr.slice(); + for (let i = 0; i < count; i++) rand[i] = arr.splice(Math.floor(Math.random() * arr.length), 1)[0]; + return rand; } /** - * Obtains a random key from this collection. This relies on the `keyArray()` method, and thus the caching mechanism - * applies here as well. - * @returns {*} + * Obtains random key(s) from this collection. This relies on {@link Collection#keyArray}, and thus the caching + * mechanism applies here as well. + * @param {number} [count] Number of keys to obtain randomly + * @returns {*|Array<*>} The single key if `count` is undefined, or an array of keys of `count` length */ - randomKey() { - const arr = this.keyArray(); - return arr[Math.floor(Math.random() * arr.length)]; + randomKey(count) { + let arr = this.keyArray(); + if (count === undefined) return arr[Math.floor(Math.random() * arr.length)]; + if (typeof count !== 'number') throw new TypeError('The count must be a number.'); + if (!Number.isInteger(count) || count < 1) throw new RangeError('The count must be an integer greater than 0.'); + if (arr.length === 0) return []; + const rand = new Array(count); + arr = arr.slice(); + for (let i = 0; i < count; i++) rand[i] = arr.splice(Math.floor(Math.random() * arr.length), 1)[0]; + return rand; } /**