'use strict';
if ( ( 0, eval )( 'this' ).XtraUtils && ( 0, eval )( 'this' ).XtraUtils.Utility ) {
let _global = ( 0, eval )( 'this' );
const XtraUtils = _global.XtraUtils;
/**
* @type {Utility}
* @property {function} aidsIn The Array class.
* @property {Array} aidsIn.prototype the Array prototype.
* @namespace
* @memberof XtraUtils
*/
XtraUtils.Array = new XtraUtils.Utility( Array );
// Some overly-large and commonly-used methods defined once.
const unique = ( b )=>{let a = [];b.forEach( ( c,b )=>{a.includes( c ) || a.push( c );} );return a;},
_compactor = item => {return isNumber( item ) ? true : Boolean( item );},
isNumber = item => typeof item === 'number',
flatten = ( c,d )=>{if( 0 === c ){return d;}let a = [];d.forEach( ( b )=>{b instanceof Array ? a = a.concat( deepClone( flatten( c - 1,b ) ) ) : a.push( deepClone( b ) );} );return a;},
compact = ( a )=>{const b = a.filter( _compactor );a.length = 0;assign( a,b );return b;},
types = [Number, String, Boolean],
deepClone = ( e )=>{let a = e;if( !a ){return a;}types.forEach( function( d ){a instanceof d && ( b = d( a ) );} );if( 'undefined' === typeof b ){if( '[object Array]' === {}.toString.call( a ) ){var b = [];a.forEach( ( a,c,e )=>{b[c] = deepClone( a );} );}else if( 'object' === typeof a ){if( a.nodeType && 'function' == typeof a.cloneNode ){b = a.cloneNode( !0 );}else if( a.prototype ){b = a;}else if( a instanceof Date ){b = new Date( a );}else{b = {};for( var c in a ){b[c] = deepClone( a[c] );}}}else {b = a;}}Object.setPrototypeOf( b,a.__proto__ );return b;},
_shallowClone = a=>[...a],
{ max, round, random, floor, ceil } = Math,
{ isArray } = Array,
{ assign, setPrototypeOf } = Object;
XtraUtils.Array.addMethod( 'range', ( function(){
/**
* Creates an array representing the range between two numbers.
* @param {number} min The lower limit on the range.
* @param {number} max The upper limit on the range.
* @param {boolean} [minInclusive=true] Whether to include `min` in the range.
* @param {boolean} [maxInclusive=true] Whether to include the maximum in the range.
* @returns {Array.<Number>} The range.
* @memberof XtraUtils.Array.
* @example
* Array.range(1, 4, true, true); // [1, 2, 3, 4]
*
* Array.range(1, 4, false, true); // [2, 3, 4]
*
* Array.range(1, 4, false, false); // [2, 3]
*/
function range( min, max, minInclusive = true, maxInclusive = true ) {
if( min > max ) {
const temp = max;
max = min;
min = temp;
}
return Array( max - min - 1 + Number( maxInclusive ) + Number( minInclusive ) ).fill( undefined ).map( ( _,i ) => Number( !minInclusive ) + i + min );
}
return range;
} )() );
XtraUtils.Array.addMethod( 'fromArrayLike', ( function(){
/**
* Returns an array that is the same as the first paramater in indices and values.
* @param {Object} arrayLike the object to create an array from.
* @returns {Array} The resulting array.
* @memberof XtraUtils.Array.
*/
function fromArrayLike( arrayLike ) {
return arrayLike.slice();
}
return fromArrayLike;
} )() );
XtraUtils.Array.addMethod( 'zip', ( function(){
const _maxLen = ( ...a ) => max( ...a.map( i=>i.length ) );
/**
* Zips the arrays so that they are parallel.
* @param {...Array} arrays The arrays to zip.
* @returns {Array.<Array>} The resulting zipped arrays.
* @memberof XtraUtils.Array.
*/
function zip( ...arrays ) {
let maxLen = _maxLen( ...arrays ), res = Array( maxLen ).fill( undefined ).map( ( i, index1 )=>{
return Array( arrays.length ).fill( undefined ).map( ( j,index2 )=>{
return arrays[index2][index1];
} );
} );
return res;
}
return zip;
} )() );
XtraUtils.Array.addMethod( 'unzip', ( function(){
const _maxLen = ( ...a ) => max( ...a.map( i=>i.length ) );
/**
* Reverses `Array.zip`.
* @param {Array} zippedArr The array to unzip.
* @returns {Array.<Array>} The resulting unzipped arrays.
* @memberof XtraUtils.Array.
*/
function unzip( zippedArr ) {
let maxLen = _maxLen( ...zippedArr ), res = Array( maxLen ).fill( undefined ).map( ( i, index1 )=>{
return Array( zippedArr.length ).fill( undefined ).map( ( j,index2 ) => {
return zippedArr[index2][index1];
} );
} );
return res;
}
return unzip;
} )() );
XtraUtils.Array.addMethod( 'diff', ( function(){
const _filter = item=>[item.before,item.after].indexOf( null ) + 1 && item.index === undefined;
/**
* Calculates the difference between two arrays.
* @param {Array} arr1 The 'old' value.
* @param {Array} arr2 The 'new' value.
* @returns {Array.<Diff>} An array of diffs.
* @memberof XtraUtils.Array.
*/
function diff( arr1, arr2 ){
arr1 = _shallowClone( arr1 ); arr2 = _shallowClone( arr2 );
let maxLength = max( arr1.length, arr2.length ), result = [];
result.length = arr1.length = arr2.length = maxLength;
result.fill( undefined ).map( ()=>( { 'before': null, 'after': null } ) );
return result.map( ( item, index ) => {
if( arr1[index] !== arr2[index] ) {
return assign( item, {
'before': arr1[index],
'after': arr2[index],
'index': index
} );
}
} ).filter( _filter );
}
return diff;
} )() );
XtraUtils.Array.addMethod( 'unique', ( function(){
/**
* Removes duplicate values in an array.
* @param {Array} arr The array to remove duplicate values from.
* @returns {Array} The uniquified array.
* @memberof XtraUtils.Array.
* @example
* Array.unique([1, 2, 3, 1, '1']); // [1, 2, 3, '1']
*/
function unique( arr ) {
let result = [];
arr.forEach( ( item, _ ) => {
if( !result.includes( item ) ){
result.push( item );
}
} );
return result;
}
return unique;
} )() );
XtraUtils.Array.addMethod( 'intersection', ( function(){
/**
* Finds all values that are in both arrays.
* @param {Array} arr1 The first array.
* @param {Array} arr2 The second array.
* @returns {Array} An array of the values that are the same.
* @memberof XtraUtils.Array.
* @example
* let myFirstArray = [1, 2, 3],
* mySecondArray = [2, 3, 4];
* Array.intersection(myFirstArray, mySecondArray); // [2, 3]
*/
function intersection( arr1, arr2 ) {
let Unique = unique( arr1 ),
result = [];
Unique.forEach( ( item, _ ) => {
if ( arr2.includes( item ) ) {
result.push( item );
}
} );
return result;
}
return intersection;
} )() );
XtraUtils.Array.addMethod( 'union', ( function(){
/**
* Returns all values in all arrays, minus duplicates.
* @param {Array} arr1 The first array.
* @param {Array} arr2 The second array.
* @returns {Array} The union of the two arrays.
* @memberof XtraUtils.Array.
* @example
* Array.union([1, 2, 3], [2, 3, 4]); // [1, 2, 3, 4]
*/
function union( arr1, arr2 ){
return unique( arr1.concat( arr2 ) );
}
return union;
} )() );
XtraUtils.Array.addUtil( 'one', ( function(){
/**
* Returns true if only one item in the array passes the test.
* @param {ArrayCallBack} fn The function.
* @param {boolean} [originalFlag=false] Whether to give the function the original array.
* @param {...*} [args] Additional arguments to give the function.
* @returns {boolean} Whether only one item in the array passes the test.
* @memberof XtraUtils.Array#
* @example
* [1, 2, 3].one((item)=>{return item === 1}); // true
*/
function one( fn, originalFlag = false, ...args ) {
const self = originalFlag ? this : _shallowClone( this );
return self.map( ( item, index ) => {
return fn( item, index, self, ...args );
} ).filter( Boolean ).length === 1;
}
return one;
} )() );
XtraUtils.Array.addMethod( 'xor', ( function(){
/**
* Returns all values that are in one array but not the other.
* @param {Array} arr1 The first array.
* @param {Array} arr2 The second array.
* @returns {Array} The logical XOR of the two arrays.
* @memberof XtraUtils.Array.
* @example
* Array.xor([1, 2, 3] [2, 3, 4]); // [1, 4]
*/
function xor( arr1, arr2 ) {
let result = [];
arr1 = unique( arr1 ),
arr2 = unique( arr2 );
arr1.forEach( ( item, _ ) => {
!arr2.includes( item ) && result.push( item );
} );
arr2.forEach( ( item, _ ) => {
!arr1.includes( item ) && result.push( item );
} );
return result;
}
return xor;
} )() );
XtraUtils.Array.addUtil( 'last', ( function(){
/**
* Gets the last `n` elements in the array.
* @param {number} [n=1] The number of elements to get off of the end of the array.
* @returns {Array} The last `n` elements in the array.
* @memberof XtraUtils.Array#
* @example
* [1, 2, 3].last(2); // [2, 3]
*/
function last( n = 1 ) {
return this.slice( this.length - n, this.length );
}
return last;
} )() );
XtraUtils.Array.addUtil( 'first', ( function(){
/**
* Gets the first `n` elements in the array.
* @param {number} [n=1] The number of elements to get off of the beginnng of the array.
* @returns {Array} The first `n` elements in the array.
* @memberof XtraUtils.Array#
* @example
* [1, 2, 3].first(2); // [1, 2]
*/
function first( n = 1 ) {
return this.slice( 0, n );
}
return first;
} )() );
XtraUtils.Array.addUtil( 'initial', ( function(){
/**
* Gets all the elements of an array *except* the last `n` elements.
* @param {number} [n=1] The number of elements to exclude.
* @returns {Array} The elements in the array except the last `n` elements.
* @memberof XtraUtils.Array#
* @example
* [1, 2, 3].initial(2); // [1]
*/
function initial( n = 1 ) {
return this.slice( 0, this.length - n );
}
return initial;
} )() );
XtraUtils.Array.addUtil( 'drop', ( function(){
/**
* Gets all elements of the array except the first `n` elements.
* @param {number} [n=1] The number of elements.
* @returns {Array} The remaining elements.
* @memberof XtraUtils.Utility
* @example
* [1, 2, 3].drop(2); // [3]
*/
function drop( n = 1 ) {
return this.slice( n, this.length );
}
return drop;
} )() );
XtraUtils.Array.addUtil( 'shuffle', ( function(){
/**
* Shuffles the elements in the array. By default does not modify the original array.
* @param {boolean} [originalFlag=false] Whether to modify the original array.
* @returns {Array} The shuffled array.
* @memberof XtraUtils.Array#
*/
function shuffle( originalFlag = false ) {
let self = originalFlag ? this : _shallowClone( this ),
i = self.length;
while ( i ) {
let j = floor( random() * i ),
t = self[--i];
self[i] = self[j];
self[j] = t;
}
return self;
}
return shuffle;
} )() );
XtraUtils.Array.addUtil( 'max', ( function(){
const _max = a=>Math.max( ...a );
/**
* Finds the maximum of the array.
* @returns {number} The largest number in the array.
* @memberof XtraUtils.Array#
* @example
* [1, 2, 3].max(); // 3
* [-1, -4, -2].max(); // -1
*/
function max() {
return _max( this );
}
return max;
} )() );
XtraUtils.Array.addUtil( 'min', ( function(){
const _min = a=>Math.min( ...a );
/**
* Finds the smallest number in the array.
* @returns {number} The smallest number in the array.
* @memberof XtraUtils.Array#
*/
function min() {
return _min( this );
}
return min;
} )() );
XtraUtils.Array.addUtil( 'deepFlatten', ( function(){
/**
* Flattens the object recursively, so that it has a depth of 0.
* @param {boolean} [originalFlag=false] Whether to modify the original array.
* @returns {Array} The smallest number in the array.
* @memberof XtraUtils.Array#
*/
function deepFlatten( originalFlag = false ){
const self = originalFlag ? this : _shallowClone( this );
return assign( self, flatten( Number.POSITIVE_INFINITY, self ) );
}
return deepFlatten;
} )() );
XtraUtils.Array.addUtil( 'chunks', ( function(){
/**
* Gets the chunks of an array, where chunks are sections of the array with a constant width.
* @param {number} chunkWidth The width of the chunks.
* @returns {Array.<Array>} The chunks of the array.
* @memberof XtraUtils.Array#
*/
function chunks( chunkWidth ) {
let result = Array( ceil( this.length / chunkWidth ) ).fill( undefined ).map( _=>[] );
this.forEach( ( item, index ) => {
result[floor( index / chunkWidth )][index % chunkWidth] = item;
} );
return result;
}
return chunks;
} )() );
XtraUtils.Array.addUtil( 'deepFlatMap', ( function(){
/**
* Maps the array, then flattens it to a depth of 0.
* @param {ArrayCallback} [fn=(..._)=>{return _[0]}] The mapping function.
* @param {boolean} [originalFlag=false] Whether to give the function the original array.
* @returns {Array} The flattend and mapped array.
* @memberof XtraUtils.Array#
*/
function deepFlatMap( fn = ( ..._ )=>{return _[0];}, originalFlag = false ) {
let self = originalFlag ? this : _shallowClone( this );
return flatten( Number.POSITIVE_INFINITY, self.map( fn ) );
}
return deepFlatMap;
} )() );
XtraUtils.Array.addUtil( 'shallowClone', ( function(){
/**
* Shallow clones the object, returning an array with the same length, and the same properties, but not equal.
* @returns {Array} The cloned array.
* @memberof XtraUtils.Array#
*/
function shallowClone() {
return setPrototypeOf( assign( [], this ), this.__proto );
}
return shallowClone;
} )() );
XtraUtils.Array.addUtil( 'sample', ( function(){
const _random = arr => arr[floor( random() * arr.length )];
/**
* Gets `n` random items from the array.
* @param {number} n How many items.
* @returns {Array.<*>} The items.
* @memberof XtraUtils.Array#
*/
function sample( n ){
let results = [], i = n,
copy = _shallowClone( this );
while( i-- ) {
let item = _random( copy );
copy.splice( copy.indexOf( item ), 1 );
results.push( item );
}
return results;
}
return sample;
} )() );
XtraUtils.Array.addUtil( 'concatMap', ( function(){
/**
* Maps the array and concatenates the results.
* @param {ArrayCallback} fn The mapper.
* @returns {Array} The mapped and concatenated result.
* @memberof XtraUtils.Array#
*/
function concatMap( fn ) {
return [].concat( ...this.map( ( item, index ) => {
let res = fn( item, index );
return isArray( res ) ? res : [res];
} ) );
}
return concatMap;
} )() );
XtraUtils.Array.addMethod( 'permutations', ( function(){
const permute = ( arr, legacy = [] ) => {
const res = [];
if( arr.length === 0 ) {
res.push( legacy );
} else {
arr.forEach( ( item, index ) => {
let current = arr.slice(),
next = current.splice( index, 1 );
res.push( ...permute( current.slice(), legacy.concat( next ) ) );
} );
}
return res;
};
/**
* Finds all permutations, or possible combinations, of the array.
* @param {Array} arr The array to find permutations of.
* @returns {Array.<Array>} An array of permutations.
* @memberof XtraUtils.Array.
*/
function permutations( arr ) {
return permute( arr );
}
return permutations;
} )() );
XtraUtils.Array.addUtil( 'mean', ( function() {
const _mean = array => {
let result = 0, total = 0;
array.forEach( function ( item ) {
if ( typeof item !== 'number' && typeof item !== 'string' ) {
throw TypeError( 'Item must be all numbers' );
}
total += Number( item );
} );
result = total / array.length;
return result;
};
/**
* Gets the mean or average of the array.
* @returns {number} The average.
* @throws {TypeError} All items of the array must be numbers, or valid number strings.
* @memberof XtraUtils.Array#
*/
function mean() {
return _mean( this );
}
return mean;
} )() );
XtraUtils.Array.addUtil( 'compact', ( function(){
const isNumber = item => typeof item === 'number',
_compactor = item => {
return isNumber( item ) ? true : Boolean( item );
};
/**
* Filters out values. Similar to `Array.prototype.filter`.
* @param {ArrayCallback} [compactor=(_)=>{return (typeof _ === 'number') ? true : Boolean(_)}] The function to compact it with.
* @param {boolean} [originalFlag=false] Whether to compact the original array.
* @returns {Array} The compacted array.
* @memberof XtraUtils.Array#
*/
function compact( compactor = _compactor, originalFlag = false ) {
const self = originalFlag ? this : _shallowClone( this );
return assign( self, self.filter( compactor ) );
}
return compact;
} )() );
XtraUtils.Array.addUtil( 'random', ( function(){
/**
* Gets a random item from the array.
* @returns {*} The item.
* @memberof XtraUtils.Array#
*/
function random() {
return this[floor( Math.random() * this.length )];
}
return random;
} )() );
XtraUtils.Array.addUtil( 'flatten', ( function(){
const _flatten = ( array, depth ) => {
if( depth === 0 ) {
return array;
}
let result = [];
array.forEach( ( item, _ ) => {
if( item instanceof Array ) {
result = result.concat( _shallowClone( _flatten( item, depth - 1 ) ) );
} else {
result.push( _shallowClone( item ) );
}
} );
return result;
};
/**
* Flattens the array by the specified amount.
* @param {number} [depth=1] The amount to flatten it by.
* @param {boolean} [originalFlag=false] Whether to modify the original array.
* @returns {Array} The flattened array.
* @memberof XtraUtils.Array#
*/
function flatten( depth = 1, originalFlag = false ) {
const self = originalFlag ? this : _shallowClone( this );
return assign( self, _flatten( self, depth ) );
}
return flatten;
} )() );
XtraUtils.Array.addUtil( 'repeat', ( function(){
/**
* Makes the array repeat `times` times.
* @param {number} times How many times to repeat the array. Can be a decimal, if so, rounds to the nearest whole number length.
* @returns {Array} The repeated array.
* @memberof XtraUtils.Array#
*/
function repeat( times ) {
const originalLength = this.length,
length = round( this.length * times ),
self = this;
return new Array( length )
.fill( undefined )
.map( ( _, i ) => {
return this[i % originalLength];
} );
}
return repeat;
} )() );
XtraUtils.Array.addUtil( 'padLeft', ( function(){
/**
* Pad an array to the left. Does not modify the original array.
* @param {number} length The length to pad to.
* @param {*} [object=0] The element to pad with.
* @returns {Array} The resulting padded array.
* @memberof XtraUtils.Array#
*/
function padLeft( length, object = 0 ) {
if( this.length >= length ) {
return this;
}
return [].repeat.call( [object], length - this.length ).concat( this );
}
return padLeft;
} )() );
XtraUtils.Array.addUtil( 'padRight', ( function(){
/**
* Pads the array, adding to the right.
* @param {number} length The length to pad to.
* @param {*} [object=0] The element to pad with.
* @returns {Array} The padded array.
* @memberof XtraUtils.Array#
*/
function padRight( length, object = 0 ){
if( this.length >= length ){
return this;
}
return this.concat( [object].repeat( length - this.length ) );
}
return padRight;
} )() );
XtraUtils.Array.addUtil( 'fillWith', ( function(){
return function fillWith( object, originalFlag = false ){
let self = originalFlag ? this : _shallowClone( this );
self.forEach( ( item, index ) => {
this[index] = typeof object === 'function' ? object( item, index, self ) : deepClone( object );
} );
return this;
};
} )() );
XtraUtils.Array.addUtil( 'where', function where( predicate = ( ..._ )=>{return _[0];}, originalFlag = false ){
let self = originalFlag ? this : _shallowClone( this ),
result = [];
self.forEach( ( item, index ) => {
if( predicate( item, index, self ) ) {
result.push( item );
}
} );
return result;
} );
XtraUtils.Array.addUtil( 'count', function count( predicate = ( ..._ )=>{return _[0];}, originalFlag = false ){
let self = originalFlag ? this : _shallowClone( this ),
Count = 0;
this.forEach( ( item, index ) => {
if( typeof predicate === 'function' ? predicate( item, index, self ) : item === predicate ) {
Count++;
}
} );
return Count;
} );
} else {
console.warn( 'XtraUtils is not defined. For more details, please visit ' +
'https://github.com/FreezePhoenix/XtraUtils/. If your issue still occurs, submit an issue here:' +
'https://github.com/FreezePhoenix/XtraUtils/issues/new' );
}
/**
* The diff object returned by `Array.diff`.
* @alias Diff
* @typedef {Object} Diff
* @property {*} before The value before changes applied.
* @property {*} after The value after changes were applied.
* @property {number} index The index in the array.
* @memberof XtraUtils.Array
*/
/**
* @alias ArrayCallback
* @callback ArrayCallback
* @param {*} item The item.
* @param {number} index The index of the item in the array.
* @param {Array} array The array being mapped.
* @returns {*} The result.
* @memberof XtraUtils.Array
*/