2025-01-02 03:13:50 +00:00
import _extends from "@babel/runtime/helpers/extends" ;
import { isInteger } from './number.js' ;
import { isNumber , isBigNumber , isArray , isString } from './is.js' ;
import { format } from './string.js' ;
import { DimensionError } from '../error/DimensionError.js' ;
import { IndexError } from '../error/IndexError.js' ;
import { deepStrictEqual } from './object.js' ;
/ * *
* Calculate the size of a multi dimensional array .
* This function checks the size of the first entry , it does not validate
* whether all dimensions match . ( use function ` validate ` for that )
* @ param { Array } x
2025-04-05 08:12:09 +00:00
* @ return { number [ ] } size
2025-01-02 03:13:50 +00:00
* /
export function arraySize ( x ) {
var s = [ ] ;
while ( Array . isArray ( x ) ) {
s . push ( x . length ) ;
x = x [ 0 ] ;
}
return s ;
}
/ * *
* Recursively validate whether each element in a multi dimensional array
* has a size corresponding to the provided size array .
* @ param { Array } array Array to be validated
* @ param { number [ ] } size Array with the size of each dimension
2025-04-05 08:12:09 +00:00
* @ param { number } dim Current dimension
2025-01-02 03:13:50 +00:00
* @ throws DimensionError
* @ private
* /
function _validate ( array , size , dim ) {
var i ;
var len = array . length ;
if ( len !== size [ dim ] ) {
throw new DimensionError ( len , size [ dim ] ) ;
}
if ( dim < size . length - 1 ) {
// recursively validate each child array
var dimNext = dim + 1 ;
for ( i = 0 ; i < len ; i ++ ) {
var child = array [ i ] ;
if ( ! Array . isArray ( child ) ) {
throw new DimensionError ( size . length - 1 , size . length , '<' ) ;
}
_validate ( array [ i ] , size , dimNext ) ;
}
} else {
2025-04-05 08:12:09 +00:00
// last dimension. none of the children may be an array
2025-01-02 03:13:50 +00:00
for ( i = 0 ; i < len ; i ++ ) {
if ( Array . isArray ( array [ i ] ) ) {
throw new DimensionError ( size . length + 1 , size . length , '>' ) ;
}
}
}
}
/ * *
* Validate whether each element in a multi dimensional array has
* a size corresponding to the provided size array .
* @ param { Array } array Array to be validated
* @ param { number [ ] } size Array with the size of each dimension
* @ throws DimensionError
* /
export function validate ( array , size ) {
var isScalar = size . length === 0 ;
if ( isScalar ) {
// scalar
if ( Array . isArray ( array ) ) {
throw new DimensionError ( array . length , 0 ) ;
}
} else {
// array
_validate ( array , size , 0 ) ;
}
}
/ * *
* Validate whether the source of the index matches the size of the Array
2025-04-05 08:12:09 +00:00
* @ param { Array | Matrix } value Array to be validated
2025-01-02 03:13:50 +00:00
* @ param { Index } index Index with the source information to validate
* @ throws DimensionError
* /
export function validateIndexSourceSize ( value , index ) {
var valueSize = value . isMatrix ? value . _size : arraySize ( value ) ;
var sourceSize = index . _sourceSize ;
// checks if the source size is not null and matches the valueSize
sourceSize . forEach ( ( sourceDim , i ) => {
if ( sourceDim !== null && sourceDim !== valueSize [ i ] ) {
throw new DimensionError ( sourceDim , valueSize [ i ] ) ;
}
} ) ;
}
/ * *
* Test whether index is an integer number with index >= 0 and index < length
* when length is provided
* @ param { number } index Zero - based index
* @ param { number } [ length ] Length of the array
* /
export function validateIndex ( index , length ) {
if ( index !== undefined ) {
if ( ! isNumber ( index ) || ! isInteger ( index ) ) {
throw new TypeError ( 'Index must be an integer (value: ' + index + ')' ) ;
}
if ( index < 0 || typeof length === 'number' && index >= length ) {
throw new IndexError ( index , length ) ;
}
}
}
/ * *
2025-04-05 08:12:09 +00:00
* Test if an index has empty values
* @ param { Index } index Zero - based index
2025-01-02 03:13:50 +00:00
* /
export function isEmptyIndex ( index ) {
for ( var i = 0 ; i < index . _dimensions . length ; ++ i ) {
var dimension = index . _dimensions [ i ] ;
if ( dimension . _data && isArray ( dimension . _data ) ) {
if ( dimension . _size [ 0 ] === 0 ) {
return true ;
}
} else if ( dimension . isRange ) {
if ( dimension . start === dimension . end ) {
return true ;
}
} else if ( isString ( dimension ) ) {
if ( dimension . length === 0 ) {
return true ;
}
}
}
return false ;
}
/ * *
* Resize a multi dimensional array . The resized array is returned .
* @ param { Array | number } array Array to be resized
* @ param { number [ ] } size Array with the size of each dimension
2025-04-05 08:12:09 +00:00
* @ param { * } [ defaultValue = 0 ] Value to be filled in new entries ,
2025-01-02 03:13:50 +00:00
* zero by default . Specify for example ` null ` ,
* to clearly see entries that are not explicitly
* set .
* @ return { Array } array The resized array
* /
export function resize ( array , size , defaultValue ) {
// check the type of the arguments
if ( ! Array . isArray ( size ) ) {
throw new TypeError ( 'Array expected' ) ;
}
if ( size . length === 0 ) {
throw new Error ( 'Resizing to scalar is not supported' ) ;
}
// check whether size contains positive integers
size . forEach ( function ( value ) {
if ( ! isNumber ( value ) || ! isInteger ( value ) || value < 0 ) {
throw new TypeError ( 'Invalid size, must contain positive integers ' + '(size: ' + format ( size ) + ')' ) ;
}
} ) ;
// convert number to an array
if ( isNumber ( array ) || isBigNumber ( array ) ) {
array = [ array ] ;
}
// recursively resize the array
var _defaultValue = defaultValue !== undefined ? defaultValue : 0 ;
_resize ( array , size , 0 , _defaultValue ) ;
return array ;
}
/ * *
* Recursively resize a multi dimensional array
* @ param { Array } array Array to be resized
* @ param { number [ ] } size Array with the size of each dimension
* @ param { number } dim Current dimension
2025-04-05 08:12:09 +00:00
* @ param { * } [ defaultValue ] Value to be filled in new entries ,
2025-01-02 03:13:50 +00:00
* undefined by default .
* @ private
* /
function _resize ( array , size , dim , defaultValue ) {
var i ;
var elem ;
var oldLen = array . length ;
var newLen = size [ dim ] ;
var minLen = Math . min ( oldLen , newLen ) ;
// apply new length
array . length = newLen ;
if ( dim < size . length - 1 ) {
// non-last dimension
var dimNext = dim + 1 ;
// resize existing child arrays
for ( i = 0 ; i < minLen ; i ++ ) {
// resize child array
elem = array [ i ] ;
if ( ! Array . isArray ( elem ) ) {
elem = [ elem ] ; // add a dimension
array [ i ] = elem ;
}
_resize ( elem , size , dimNext , defaultValue ) ;
}
// create new child arrays
for ( i = minLen ; i < newLen ; i ++ ) {
// get child array
elem = [ ] ;
array [ i ] = elem ;
// resize new child array
_resize ( elem , size , dimNext , defaultValue ) ;
}
} else {
// last dimension
// remove dimensions of existing values
for ( i = 0 ; i < minLen ; i ++ ) {
while ( Array . isArray ( array [ i ] ) ) {
array [ i ] = array [ i ] [ 0 ] ;
}
}
// fill new elements with the default value
for ( i = minLen ; i < newLen ; i ++ ) {
array [ i ] = defaultValue ;
}
}
}
/ * *
* Re - shape a multi dimensional array to fit the specified dimensions
* @ param { Array } array Array to be reshaped
* @ param { number [ ] } sizes List of sizes for each dimension
* @ returns { Array } Array whose data has been formatted to fit the
* specified dimensions
*
* @ throws { DimensionError } If the product of the new dimension sizes does
* not equal that of the old ones
* /
export function reshape ( array , sizes ) {
2025-04-05 08:12:09 +00:00
var flatArray = flatten ( array , true ) ; // since it has rectangular
2025-01-02 03:13:50 +00:00
var currentLength = flatArray . length ;
if ( ! Array . isArray ( array ) || ! Array . isArray ( sizes ) ) {
throw new TypeError ( 'Array expected' ) ;
}
if ( sizes . length === 0 ) {
throw new DimensionError ( 0 , currentLength , '!=' ) ;
}
sizes = processSizesWildcard ( sizes , currentLength ) ;
var newLength = product ( sizes ) ;
if ( currentLength !== newLength ) {
throw new DimensionError ( newLength , currentLength , '!=' ) ;
}
try {
return _reshape ( flatArray , sizes ) ;
} catch ( e ) {
if ( e instanceof DimensionError ) {
throw new DimensionError ( newLength , currentLength , '!=' ) ;
}
throw e ;
}
}
/ * *
* Replaces the wildcard - 1 in the sizes array .
2025-04-05 08:12:09 +00:00
* @ param { number [ ] } sizes List of sizes for each dimension . At most one wildcard .
2025-01-02 03:13:50 +00:00
* @ param { number } currentLength Number of elements in the array .
* @ throws { Error } If more than one wildcard or unable to replace it .
* @ returns { number [ ] } The sizes array with wildcard replaced .
* /
export function processSizesWildcard ( sizes , currentLength ) {
var newLength = product ( sizes ) ;
var processedSizes = sizes . slice ( ) ;
var WILDCARD = - 1 ;
var wildCardIndex = sizes . indexOf ( WILDCARD ) ;
var isMoreThanOneWildcard = sizes . indexOf ( WILDCARD , wildCardIndex + 1 ) >= 0 ;
if ( isMoreThanOneWildcard ) {
throw new Error ( 'More than one wildcard in sizes' ) ;
}
var hasWildcard = wildCardIndex >= 0 ;
var canReplaceWildcard = currentLength % newLength === 0 ;
if ( hasWildcard ) {
if ( canReplaceWildcard ) {
processedSizes [ wildCardIndex ] = - currentLength / newLength ;
} else {
throw new Error ( 'Could not replace wildcard, since ' + currentLength + ' is no multiple of ' + - newLength ) ;
}
}
return processedSizes ;
}
/ * *
* Computes the product of all array elements .
* @ param { number [ ] } array Array of factors
* @ returns { number } Product of all elements
* /
function product ( array ) {
return array . reduce ( ( prev , curr ) => prev * curr , 1 ) ;
}
/ * *
* Iteratively re - shape a multi dimensional array to fit the specified dimensions
* @ param { Array } array Array to be reshaped
* @ param { number [ ] } sizes List of sizes for each dimension
* @ returns { Array } Array whose data has been formatted to fit the
* specified dimensions
* /
function _reshape ( array , sizes ) {
// testing if there are enough elements for the requested shape
var tmpArray = array ;
var tmpArray2 ;
2025-04-05 08:12:09 +00:00
// for each dimension starting by the last one and ignoring the first one
2025-01-02 03:13:50 +00:00
for ( var sizeIndex = sizes . length - 1 ; sizeIndex > 0 ; sizeIndex -- ) {
var size = sizes [ sizeIndex ] ;
tmpArray2 = [ ] ;
// aggregate the elements of the current tmpArray in elements of the requested size
var length = tmpArray . length / size ;
for ( var i = 0 ; i < length ; i ++ ) {
tmpArray2 . push ( tmpArray . slice ( i * size , ( i + 1 ) * size ) ) ;
}
// set it as the new tmpArray for the next loop turn or for return
tmpArray = tmpArray2 ;
}
return tmpArray ;
}
/ * *
* Squeeze a multi dimensional array
* @ param { Array } array
* @ param { Array } [ size ]
* @ returns { Array } returns the array itself
* /
export function squeeze ( array , size ) {
var s = size || arraySize ( array ) ;
// squeeze outer dimensions
while ( Array . isArray ( array ) && array . length === 1 ) {
array = array [ 0 ] ;
s . shift ( ) ;
}
// find the first dimension to be squeezed
var dims = s . length ;
while ( s [ dims - 1 ] === 1 ) {
dims -- ;
}
// squeeze inner dimensions
if ( dims < s . length ) {
array = _squeeze ( array , dims , 0 ) ;
s . length = dims ;
}
return array ;
}
/ * *
* Recursively squeeze a multi dimensional array
* @ param { Array } array
* @ param { number } dims Required number of dimensions
* @ param { number } dim Current dimension
* @ returns { Array | * } Returns the squeezed array
* @ private
* /
function _squeeze ( array , dims , dim ) {
var i , ii ;
if ( dim < dims ) {
var next = dim + 1 ;
for ( i = 0 , ii = array . length ; i < ii ; i ++ ) {
array [ i ] = _squeeze ( array [ i ] , dims , next ) ;
}
} else {
while ( Array . isArray ( array ) ) {
array = array [ 0 ] ;
}
}
return array ;
}
/ * *
* Unsqueeze a multi dimensional array : add dimensions when missing
*
2025-04-05 08:12:09 +00:00
* Parameter ` size ` will be mutated to match the new , unsqueezed matrix size .
2025-01-02 03:13:50 +00:00
*
* @ param { Array } array
* @ param { number } dims Desired number of dimensions of the array
* @ param { number } [ outer ] Number of outer dimensions to be added
* @ param { Array } [ size ] Current size of array .
* @ returns { Array } returns the array itself
* @ private
* /
export function unsqueeze ( array , dims , outer , size ) {
var s = size || arraySize ( array ) ;
// unsqueeze outer dimensions
if ( outer ) {
for ( var i = 0 ; i < outer ; i ++ ) {
array = [ array ] ;
s . unshift ( 1 ) ;
}
}
// unsqueeze inner dimensions
array = _unsqueeze ( array , dims , 0 ) ;
while ( s . length < dims ) {
s . push ( 1 ) ;
}
return array ;
}
/ * *
* Recursively unsqueeze a multi dimensional array
* @ param { Array } array
* @ param { number } dims Required number of dimensions
* @ param { number } dim Current dimension
2025-04-05 08:12:09 +00:00
* @ returns { Array | * } Returns the unsqueezed array
2025-01-02 03:13:50 +00:00
* @ private
* /
function _unsqueeze ( array , dims , dim ) {
var i , ii ;
if ( Array . isArray ( array ) ) {
var next = dim + 1 ;
for ( i = 0 , ii = array . length ; i < ii ; i ++ ) {
array [ i ] = _unsqueeze ( array [ i ] , dims , next ) ;
}
} else {
for ( var d = dim ; d < dims ; d ++ ) {
array = [ array ] ;
}
}
return array ;
}
/ * *
* Flatten a multi dimensional array , put all elements in a one dimensional
* array
* @ param { Array } array A multi dimensional array
2025-04-05 08:12:09 +00:00
* @ param { boolean } isRectangular Optional . If the array is rectangular ( not jagged )
2025-01-02 03:13:50 +00:00
* @ return { Array } The flattened array ( 1 dimensional )
* /
export function flatten ( array ) {
2025-04-05 08:12:09 +00:00
var isRectangular = arguments . length > 1 && arguments [ 1 ] !== undefined ? arguments [ 1 ] : false ;
2025-01-02 03:13:50 +00:00
if ( ! Array . isArray ( array ) ) {
// if not an array, return as is
return array ;
}
2025-04-05 08:12:09 +00:00
if ( typeof isRectangular !== 'boolean' ) {
throw new TypeError ( 'Boolean expected for second argument of flatten' ) ;
}
2025-01-02 03:13:50 +00:00
var flat = [ ] ;
2025-04-05 08:12:09 +00:00
if ( isRectangular ) {
_flattenRectangular ( array ) ;
} else {
_flatten ( array ) ;
}
return flat ;
function _flatten ( array ) {
for ( var i = 0 ; i < array . length ; i ++ ) {
var item = array [ i ] ;
if ( Array . isArray ( item ) ) {
_flatten ( item ) ;
} else {
flat . push ( item ) ;
}
}
}
function _flattenRectangular ( array ) {
if ( Array . isArray ( array [ 0 ] ) ) {
for ( var i = 0 ; i < array . length ; i ++ ) {
_flattenRectangular ( array [ i ] ) ;
}
2025-01-02 03:13:50 +00:00
} else {
2025-04-05 08:12:09 +00:00
for ( var _i = 0 ; _i < array . length ; _i ++ ) {
flat . push ( array [ _i ] ) ;
}
2025-01-02 03:13:50 +00:00
}
2025-04-05 08:12:09 +00:00
}
2025-01-02 03:13:50 +00:00
}
/ * *
* A safe map
* @ param { Array } array
* @ param { function } callback
* /
export function map ( array , callback ) {
return Array . prototype . map . call ( array , callback ) ;
}
/ * *
* A safe forEach
* @ param { Array } array
* @ param { function } callback
* /
export function forEach ( array , callback ) {
Array . prototype . forEach . call ( array , callback ) ;
}
/ * *
* A safe filter
* @ param { Array } array
* @ param { function } callback
* /
export function filter ( array , callback ) {
if ( arraySize ( array ) . length !== 1 ) {
throw new Error ( 'Only one dimensional matrices supported' ) ;
}
return Array . prototype . filter . call ( array , callback ) ;
}
/ * *
2025-04-05 08:12:09 +00:00
* Filter values in an array given a regular expression
2025-01-02 03:13:50 +00:00
* @ param { Array } array
* @ param { RegExp } regexp
* @ return { Array } Returns the filtered array
* @ private
* /
export function filterRegExp ( array , regexp ) {
if ( arraySize ( array ) . length !== 1 ) {
throw new Error ( 'Only one dimensional matrices supported' ) ;
}
return Array . prototype . filter . call ( array , entry => regexp . test ( entry ) ) ;
}
/ * *
* A safe join
* @ param { Array } array
* @ param { string } separator
* /
export function join ( array , separator ) {
return Array . prototype . join . call ( array , separator ) ;
}
/ * *
* Assign a numeric identifier to every element of a sorted array
* @ param { Array } a An array
* @ return { Array } An array of objects containing the original value and its identifier
* /
export function identify ( a ) {
if ( ! Array . isArray ( a ) ) {
throw new TypeError ( 'Array input expected' ) ;
}
if ( a . length === 0 ) {
return a ;
}
var b = [ ] ;
var count = 0 ;
b [ 0 ] = {
value : a [ 0 ] ,
identifier : 0
} ;
for ( var i = 1 ; i < a . length ; i ++ ) {
if ( a [ i ] === a [ i - 1 ] ) {
count ++ ;
} else {
count = 0 ;
}
b . push ( {
value : a [ i ] ,
identifier : count
} ) ;
}
return b ;
}
/ * *
* Remove the numeric identifier from the elements
* @ param { array } a An array
* @ return { array } An array of values without identifiers
* /
export function generalize ( a ) {
if ( ! Array . isArray ( a ) ) {
throw new TypeError ( 'Array input expected' ) ;
}
if ( a . length === 0 ) {
return a ;
}
var b = [ ] ;
for ( var i = 0 ; i < a . length ; i ++ ) {
b . push ( a [ i ] . value ) ;
}
return b ;
}
/ * *
* Check the datatype of a given object
* This is a low level implementation that should only be used by
* parent Matrix classes such as SparseMatrix or DenseMatrix
* This method does not validate Array Matrix shape
* @ param { Array } array
* @ param { function } typeOf Callback function to use to determine the type of a value
* @ return { string }
* /
export function getArrayDataType ( array , typeOf ) {
var type ; // to hold type info
var length = 0 ; // to hold length value to ensure it has consistent sizes
for ( var i = 0 ; i < array . length ; i ++ ) {
var item = array [ i ] ;
var _isArray = Array . isArray ( item ) ;
// Saving the target matrix row size
if ( i === 0 && _isArray ) {
length = item . length ;
}
// If the current item is an array but the length does not equal the targetVectorSize
if ( _isArray && item . length !== length ) {
return undefined ;
}
var itemType = _isArray ? getArrayDataType ( item , typeOf ) // recurse into a nested array
: typeOf ( item ) ;
if ( type === undefined ) {
type = itemType ; // first item
} else if ( type !== itemType ) {
return 'mixed' ;
} else {
// we're good, everything has the same type so far
}
}
return type ;
}
/ * *
* Return the last item from an array
2025-04-05 08:12:09 +00:00
* @ param { Array } array
2025-01-02 03:13:50 +00:00
* @ returns { * }
* /
export function last ( array ) {
return array [ array . length - 1 ] ;
}
/ * *
* Get all but the last element of array .
2025-04-05 08:12:09 +00:00
* @ param { Array } array
* @ returns { Array }
2025-01-02 03:13:50 +00:00
* /
export function initial ( array ) {
return array . slice ( 0 , array . length - 1 ) ;
}
/ * *
* Recursively concatenate two matrices .
2025-04-05 08:12:09 +00:00
* The contents of the matrices are not cloned .
2025-01-02 03:13:50 +00:00
* @ param { Array } a Multi dimensional array
* @ param { Array } b Multi dimensional array
* @ param { number } concatDim The dimension on which to concatenate ( zero - based )
* @ param { number } dim The current dim ( zero - based )
* @ return { Array } c The concatenated matrix
* @ private
* /
function concatRecursive ( a , b , concatDim , dim ) {
if ( dim < concatDim ) {
// recurse into next dimension
if ( a . length !== b . length ) {
throw new DimensionError ( a . length , b . length ) ;
}
var c = [ ] ;
for ( var i = 0 ; i < a . length ; i ++ ) {
c [ i ] = concatRecursive ( a [ i ] , b [ i ] , concatDim , dim + 1 ) ;
}
return c ;
} else {
// concatenate this dimension
return a . concat ( b ) ;
}
}
/ * *
* Concatenates many arrays in the specified direction
* @ param { ... Array } arrays All the arrays to concatenate
* @ param { number } concatDim The dimension on which to concatenate ( zero - based )
2025-04-05 08:12:09 +00:00
* @ returns { Array }
* /
2025-01-02 03:13:50 +00:00
export function concat ( ) {
var arrays = Array . prototype . slice . call ( arguments , 0 , - 1 ) ;
var concatDim = Array . prototype . slice . call ( arguments , - 1 ) ;
if ( arrays . length === 1 ) {
return arrays [ 0 ] ;
}
if ( arrays . length > 1 ) {
return arrays . slice ( 1 ) . reduce ( function ( A , B ) {
return concatRecursive ( A , B , concatDim , 0 ) ;
} , arrays [ 0 ] ) ;
} else {
throw new Error ( 'Wrong number of arguments in function concat' ) ;
}
}
/ * *
2025-04-05 08:12:09 +00:00
* Receives two or more sizes and gets the broadcasted size for both .
2025-01-02 03:13:50 +00:00
* @ param { ... number [ ] } sizes Sizes to broadcast together
2025-04-05 08:12:09 +00:00
* @ returns { number [ ] } The broadcasted size
2025-01-02 03:13:50 +00:00
* /
export function broadcastSizes ( ) {
for ( var _len = arguments . length , sizes = new Array ( _len ) , _key = 0 ; _key < _len ; _key ++ ) {
sizes [ _key ] = arguments [ _key ] ;
}
var dimensions = sizes . map ( s => s . length ) ;
var N = Math . max ( ... dimensions ) ;
var sizeMax = new Array ( N ) . fill ( null ) ;
// check for every size
for ( var i = 0 ; i < sizes . length ; i ++ ) {
var size = sizes [ i ] ;
var dim = dimensions [ i ] ;
for ( var j = 0 ; j < dim ; j ++ ) {
var n = N - dim + j ;
if ( size [ j ] > sizeMax [ n ] ) {
sizeMax [ n ] = size [ j ] ;
}
}
}
2025-04-05 08:12:09 +00:00
for ( var _i2 = 0 ; _i2 < sizes . length ; _i2 ++ ) {
checkBroadcastingRules ( sizes [ _i2 ] , sizeMax ) ;
2025-01-02 03:13:50 +00:00
}
return sizeMax ;
}
/ * *
* Checks if it ' s possible to broadcast a size to another size
* @ param { number [ ] } size The size of the array to check
* @ param { number [ ] } toSize The size of the array to validate if it can be broadcasted to
* /
export function checkBroadcastingRules ( size , toSize ) {
var N = toSize . length ;
var dim = size . length ;
for ( var j = 0 ; j < dim ; j ++ ) {
var n = N - dim + j ;
if ( size [ j ] < toSize [ n ] && size [ j ] > 1 || size [ j ] > toSize [ n ] ) {
2025-04-05 08:12:09 +00:00
throw new Error ( "shape mismatch: mismatch is found in arg with shape (" . concat ( size , ") not possible to broadcast dimension " ) . concat ( dim , " with size " ) . concat ( size [ j ] , " to size " ) . concat ( toSize [ n ] ) ) ;
2025-01-02 03:13:50 +00:00
}
}
}
/ * *
* Broadcasts a single array to a certain size
2025-04-05 08:12:09 +00:00
* @ param { Array } array Array to be broadcasted
2025-01-02 03:13:50 +00:00
* @ param { number [ ] } toSize Size to broadcast the array
2025-04-05 08:12:09 +00:00
* @ returns { Array } The broadcasted array
2025-01-02 03:13:50 +00:00
* /
export function broadcastTo ( array , toSize ) {
var Asize = arraySize ( array ) ;
if ( deepStrictEqual ( Asize , toSize ) ) {
return array ;
}
checkBroadcastingRules ( Asize , toSize ) ;
var broadcastedSize = broadcastSizes ( Asize , toSize ) ;
var N = broadcastedSize . length ;
var paddedSize = [ ... Array ( N - Asize . length ) . fill ( 1 ) , ... Asize ] ;
var A = clone ( array ) ;
// reshape A if needed to make it ready for concat
if ( Asize . length < N ) {
A = reshape ( A , paddedSize ) ;
Asize = arraySize ( A ) ;
}
// stretches the array on each dimension to make it the same size as index
for ( var dim = 0 ; dim < N ; dim ++ ) {
if ( Asize [ dim ] < broadcastedSize [ dim ] ) {
A = stretch ( A , broadcastedSize [ dim ] , dim ) ;
Asize = arraySize ( A ) ;
}
}
return A ;
}
/ * *
* Broadcasts arrays and returns the broadcasted arrays in an array
* @ param { ... Array | any } arrays
2025-04-05 08:12:09 +00:00
* @ returns { Array [ ] } The broadcasted arrays
2025-01-02 03:13:50 +00:00
* /
export function broadcastArrays ( ) {
for ( var _len2 = arguments . length , arrays = new Array ( _len2 ) , _key2 = 0 ; _key2 < _len2 ; _key2 ++ ) {
arrays [ _key2 ] = arguments [ _key2 ] ;
}
if ( arrays . length === 0 ) {
2025-04-05 08:12:09 +00:00
throw new Error ( 'Insufficient number of arguments in function broadcastArrays' ) ;
2025-01-02 03:13:50 +00:00
}
if ( arrays . length === 1 ) {
return arrays [ 0 ] ;
}
var sizes = arrays . map ( function ( array ) {
return arraySize ( array ) ;
} ) ;
var broadcastedSize = broadcastSizes ( ... sizes ) ;
var broadcastedArrays = [ ] ;
arrays . forEach ( function ( array ) {
broadcastedArrays . push ( broadcastTo ( array , broadcastedSize ) ) ;
} ) ;
return broadcastedArrays ;
}
/ * *
2025-04-05 08:12:09 +00:00
* Stretches a matrix up to a certain size in a certain dimension
2025-01-02 03:13:50 +00:00
* @ param { Array } arrayToStretch
* @ param { number [ ] } sizeToStretch
* @ param { number } dimToStretch
2025-04-05 08:12:09 +00:00
* @ returns { Array } The stretched array
2025-01-02 03:13:50 +00:00
* /
export function stretch ( arrayToStretch , sizeToStretch , dimToStretch ) {
return concat ( ... Array ( sizeToStretch ) . fill ( arrayToStretch ) , dimToStretch ) ;
}
/ * *
* Retrieves a single element from an array given an index .
*
* @ param { Array } array - The array from which to retrieve the value .
2025-04-05 08:12:09 +00:00
* @ param { Array < number > } index - An array of indices specifying the position of the desired element in each dimension .
2025-01-02 03:13:50 +00:00
* @ returns { * } - The value at the specified position in the array .
*
* @ example
* const arr = [ [ [ 1 , 2 ] , [ 3 , 4 ] ] , [ [ 5 , 6 ] , [ 7 , 8 ] ] ] ;
* const index = [ 1 , 0 , 1 ] ;
2025-04-05 08:12:09 +00:00
* console . log ( get ( arr , index ) ) ; // 6
2025-01-02 03:13:50 +00:00
* /
export function get ( array , index ) {
if ( ! Array . isArray ( array ) ) {
throw new Error ( 'Array expected' ) ;
}
var size = arraySize ( array ) ;
if ( index . length !== size . length ) {
throw new DimensionError ( index . length , size . length ) ;
}
for ( var x = 0 ; x < index . length ; x ++ ) {
validateIndex ( index [ x ] , size [ x ] ) ;
}
return index . reduce ( ( acc , curr ) => acc [ curr ] , array ) ;
}
/ * *
2025-04-05 08:12:09 +00:00
* Recursively maps over each element of nested array using a provided callback function .
2025-01-02 03:13:50 +00:00
*
2025-04-05 08:12:09 +00:00
* @ param { Array } array - The array to be mapped .
* @ param { Function } callback - The function to execute on each element , taking three arguments :
* - ` value ` ( any ) : The current element being processed in the array .
* - ` index ` ( Array < number > ) : The index of the current element being processed in the array .
* - ` array ` ( Array ) : The array ` deepMap ` was called upon .
* @ param { boolean } [ skipIndex = false ] - If true , the callback function is called with only the value .
* @ returns { Array } A new array with each element being the result of the callback function .
* /
export function deepMap ( array , callback ) {
var skipIndex = arguments . length > 2 && arguments [ 2 ] !== undefined ? arguments [ 2 ] : false ;
if ( array . length === 0 ) {
return [ ] ;
}
if ( skipIndex ) {
return recursiveMap ( array ) ;
}
var index = [ ] ;
return recursiveMapWithIndex ( array , 0 ) ;
function recursiveMapWithIndex ( value , depth ) {
if ( Array . isArray ( value ) ) {
var N = value . length ;
var result = Array ( N ) ;
for ( var i = 0 ; i < N ; i ++ ) {
index [ depth ] = i ;
result [ i ] = recursiveMapWithIndex ( value [ i ] , depth + 1 ) ;
}
return result ;
} else {
return callback ( value , index . slice ( 0 , depth ) , array ) ;
}
}
function recursiveMap ( value ) {
if ( Array . isArray ( value ) ) {
var N = value . length ;
var result = Array ( N ) ;
for ( var i = 0 ; i < N ; i ++ ) {
result [ i ] = recursiveMap ( value [ i ] ) ;
}
return result ;
} else {
return callback ( value ) ;
}
}
}
/ * *
* Recursively iterates over each element in a multi - dimensional array and applies a callback function .
*
* @ param { Array } array - The multi - dimensional array to iterate over .
* @ param { Function } callback - The function to execute for each element . It receives three arguments :
* - { any } value : The current element being processed in the array .
* - { Array < number > } index : The index of the current element in each dimension .
* - { Array } array : The original array being processed .
* @ param { boolean } [ skipIndex = false ] - If true , the callback function is called with only the value .
* /
export function deepForEach ( array , callback ) {
var skipIndex = arguments . length > 2 && arguments [ 2 ] !== undefined ? arguments [ 2 ] : false ;
if ( array . length === 0 ) {
return ;
}
if ( skipIndex ) {
recursiveForEach ( array ) ;
return ;
}
var index = [ ] ;
recursiveForEachWithIndex ( array , 0 ) ;
function recursiveForEachWithIndex ( value , depth ) {
if ( Array . isArray ( value ) ) {
var N = value . length ;
for ( var i = 0 ; i < N ; i ++ ) {
index [ depth ] = i ;
recursiveForEachWithIndex ( value [ i ] , depth + 1 ) ;
}
} else {
callback ( value , index . slice ( 0 , depth ) , array ) ;
}
}
function recursiveForEach ( value ) {
if ( Array . isArray ( value ) ) {
var N = value . length ;
for ( var i = 0 ; i < N ; i ++ ) {
recursiveForEach ( value [ i ] ) ;
}
} else {
callback ( value ) ;
}
2025-01-02 03:13:50 +00:00
}
}
/ * *
* Deep clones a multidimensional array
* @ param { Array } array
2025-04-05 08:12:09 +00:00
* @ returns { Array } cloned array
2025-01-02 03:13:50 +00:00
* /
export function clone ( array ) {
return _extends ( [ ] , array ) ;
}