UNPKG

@tsdotnet/array-utility

Version:

A small collection of useful array functions.

332 lines 11.4 kB
"use strict"; /*! * @author electricessence / https://github.com/electricessence/ * @license MIT */ Object.defineProperty(exports, "__esModule", { value: true }); exports.flatten = exports.distinct = exports.rangeUntil = exports.range = exports.repeat = exports.remove = exports.removeIndex = exports.applyTo = exports.forEach = exports.findIndex = exports.register = exports.clearEach = exports.updateRange = exports.replace = exports.contains = exports.indexOf = exports.copyTo = exports.copy = exports.init = void 0; const tslib_1 = require("tslib"); const ArgumentOutOfRangeException_1 = (0, tslib_1.__importDefault)(require("@tsdotnet/exceptions/dist/ArgumentOutOfRangeException")); const ArgumentNullException_1 = (0, tslib_1.__importDefault)(require("@tsdotnet/exceptions/dist/ArgumentNullException")); const ArgumentException_1 = (0, tslib_1.__importDefault)(require("@tsdotnet/exceptions/dist/ArgumentException")); const array_init_1 = (0, tslib_1.__importDefault)(require("@tsdotnet/array-init")); exports.init = array_init_1.default; const type_1 = (0, tslib_1.__importDefault)(require("@tsdotnet/type")); const array_copy_1 = (0, tslib_1.__importStar)(require("@tsdotnet/array-copy")); exports.copy = array_copy_1.default; Object.defineProperty(exports, "copyTo", { enumerable: true, get: function () { return array_copy_1.arrayCopyTo; } }); const integer_1 = (0, tslib_1.__importDefault)(require("@tsdotnet/integer")); const areEqual_1 = (0, tslib_1.__importDefault)(require("@tsdotnet/compare/dist/areEqual")); const CBN = 'Cannot be null.', CB0 = 'Cannot be zero.', CBL0 = 'Cannot be less than zero.', VFN = 'Must be a valid finite number'; /** * Checks to see where the provided array contains an item/value. * If the array value is null, then -1 is returned. * @param array * @param item * @param {function?} equalityComparer * @returns {number} */ function indexOf(array, item, equalityComparer = areEqual_1.default) { const len = array && array.length; if (len) { // NaN NEVER evaluates its equality so be careful. if (equalityComparer === areEqual_1.default && array instanceof Array && !type_1.default.isTrueNaN(item)) return array.indexOf(item); for (let i = 0; i < len; i++) { // 'areEqual' includes NaN==NaN evaluation. if (equalityComparer(array[i], item)) return i; } } return -1; } exports.indexOf = indexOf; /** * Checks to see if the provided array contains an item. * If the array value is null, then false is returned. * @param array * @param item * @param {function?} equalityComparer * @returns {boolean} */ function contains(array, item, equalityComparer = areEqual_1.default) { return indexOf(array, item, equalityComparer) !== -1; } exports.contains = contains; /** * Finds and replaces a value from an array. Will replaces all instances unless a maximum is specified. * @param array * @param old * @param newValue * @param max * @returns {number} */ function replace(array, old, newValue, max = Infinity) { if (!array || !array.length || max === 0) return 0; if (max < 0) throw new ArgumentOutOfRangeException_1.default('max', max, CBL0); if (!max) max = Infinity; // just in case. let count = 0; for (let i = 0, len = array.length; i < len; i++) { if (array[i] === old) { array[i] = newValue; ++count; if (count === max) break; } } return count; } exports.replace = replace; /** * Replaces values of an array across a range of indexes. * @param array * @param value * @param start * @param stop */ function updateRange(array, value, start = 0, stop) { if (!array) return; integer_1.default.assertZeroOrGreater(start, 'start'); if (!stop && stop !== 0) stop = array.length; integer_1.default.assert(stop, 'stop'); if (stop < start) throw new ArgumentOutOfRangeException_1.default('stop', stop, 'is less than start'); for (let i = start; i < stop; i++) { array[i] = value; } } exports.updateRange = updateRange; /** * Clears (sets to null) values of an array across a range of indexes. * @param array * @param start * @param stop */ function clearEach(array, start = 0, stop) { updateRange(array, null, start, stop); } exports.clearEach = clearEach; /** * Ensures a value exists within an array. If not found, adds to the end. * @param array * @param item * @param {function?} equalityComparer * @returns {boolean} */ function register(array, item, equalityComparer = areEqual_1.default) { if (!array) throw new ArgumentNullException_1.default('array', CBN); const len = array.length; // avoid querying .length more than once. * const ok = !len || !contains(array, item, equalityComparer); if (ok) array[len] = item; // * push would query length again. return ok; } exports.register = register; /** * Returns the first index of which the provided predicate returns true. * Returns -1 if always false. * @param array * @param predicate * @returns {number} */ function findIndex(array, predicate) { if (!array) throw new ArgumentNullException_1.default('array', CBN); if (!type_1.default.isFunction(predicate)) throw new ArgumentException_1.default('predicate', 'Must be a function.'); const len = array.length; if (!type_1.default.isNumber(len, true) || len < 0) throw new ArgumentException_1.default('array', 'Does not have a valid length.'); if (array instanceof Array) { for (let i = 0; i < len; i++) { if (predicate(array[i], i)) return i; } } else { for (let i = 0; i < len; i++) { if (i in array && predicate(array[i], i)) return i; } } return -1; } exports.findIndex = findIndex; /** * Allows for using "false" to cause forEach to break. * Can also be applied to a structure that indexes like an array, but may not be. * @param source * @param action */ function forEach(source, action) { if (source && action != null) { // Don't cache the length since it is possible that the underlying array changed. for (let i = 0; i < source.length; i++) { // noinspection PointlessBooleanExpressionJS if (action(source[i], i) === false) break; } } } exports.forEach = forEach; /** * Is similar to Array.map() but instead of returning a new array, it updates the existing indexes. * Can also be applied to a structure that indexes like an array, but may not be. * @param target * @param fn */ function applyTo(target, fn) { if (target && fn != null) { for (let i = 0; i < target.length; i++) { target[i] = fn(target[i], i); } } } exports.applyTo = applyTo; /** * Removes an entry at a specified index. * @param array * @param index * @returns {boolean} True if the value was able to be removed. */ function removeIndex(array, index) { if (!array) throw new ArgumentNullException_1.default('array', CBN); integer_1.default.assert(index, 'index'); if (index < 0) throw new ArgumentOutOfRangeException_1.default('index', index, CBL0); const exists = index < array.length; if (exists) array.splice(index, 1); return exists; } exports.removeIndex = removeIndex; /** * Finds and removes a value from an array. Will remove all instances unless a maximum is specified. * @param array * @param value * @param max * @param {function?} equalityComparer * @returns {number} The number of times the value was found and removed. */ function remove(array, value, max = Infinity, equalityComparer = areEqual_1.default) { if (!array || !array.length || max === 0) return 0; if (max < 0) throw new ArgumentOutOfRangeException_1.default('max', max, CBL0); let count = 0; if (!max || !isFinite(max)) { // Don't track the indexes and remove in reverse. for (let i = array.length - 1; i >= 0; i--) { if (equalityComparer(array[i], value)) { array.splice(i, 1); ++count; } } } else { // Since the user will expect it to happen in forward order... const found = []; // indexes; for (let i = 0, len = array.length; i < len; i++) { if (equalityComparer(array[i], value)) { found.push(i); ++count; if (count === max) break; } } for (let i = found.length - 1; i >= 0; i--) { array.splice(found[i], 1); } } return count; } exports.remove = remove; /** * Simply repeats a value the number of times specified. * @param element * @param count * @returns {T[]} */ function repeat(element, count) { integer_1.default.assert(count, 'count'); if (count < 0) throw new ArgumentOutOfRangeException_1.default('count', count, CBL0); const result = (0, array_init_1.default)(count); for (let i = 0; i < count; i++) { result[i] = element; } return result; } exports.repeat = repeat; /** * Returns a range of numbers based upon the first value and the step value. * @param first * @param count * @param step * @returns {number[]} */ function range(first, count, step = 1) { if (isNaN(first) || !isFinite(first)) throw new ArgumentOutOfRangeException_1.default('first', first, VFN); if (isNaN(count) || !isFinite(count)) throw new ArgumentOutOfRangeException_1.default('count', count, VFN); if (count < 0) throw new ArgumentOutOfRangeException_1.default('count', count, CBL0); const result = (0, array_init_1.default)(count); for (let i = 0; i < count; i++) { result[i] = first; first += step; } return result; } exports.range = range; /** * Returns a range of numbers based upon the first value and the step value excluding any numbers at or beyond the until value. * @param first * @param until * @param step * @returns {number[]} */ function rangeUntil(first, until, step = 1) { if (step === 0) throw new ArgumentOutOfRangeException_1.default('step', step, CB0); return range(first, (until - first) / step, step); } exports.rangeUntil = rangeUntil; function distinct(source) { if (!source) return []; // Allowing for null facilitates regex filtering. const seen = {}; return source.filter((e) => !(e in seen) && (seen[e] = true)); } exports.distinct = distinct; /** * Takes any arrays within an array and inserts the values contained within in place of that array. * For every count higher than 0 in recurseDepth it will attempt an additional pass. Passing Infinity will flatten all arrays contained. * @param a * @param recurseDepth * @returns {any[]} */ function flatten(a, recurseDepth = 0) { const result = []; for (let x of a) { if (x instanceof Array) { if (recurseDepth > 0) x = flatten(x, recurseDepth - 1); for (const e of x) result.push(e); } else result.push(x); } return result; } exports.flatten = flatten; //# sourceMappingURL=arrayUtility.js.map