UNPKG

5.31 kBJavaScriptView Raw
1import { isCollection, isMatrix } from './is'
2import { IndexError } from '../error/IndexError'
3import { arraySize } from './array'
4
5/**
6 * Test whether an array contains collections
7 * @param {Array} array
8 * @returns {boolean} Returns true when the array contains one or multiple
9 * collections (Arrays or Matrices). Returns false otherwise.
10 */
11export function containsCollections (array) {
12 for (let i = 0; i < array.length; i++) {
13 if (isCollection(array[i])) {
14 return true
15 }
16 }
17 return false
18}
19
20/**
21 * Recursively loop over all elements in a given multi dimensional array
22 * and invoke the callback on each of the elements.
23 * @param {Array | Matrix} array
24 * @param {Function} callback The callback method is invoked with one
25 * parameter: the current element in the array
26 */
27export function deepForEach (array, callback) {
28 if (isMatrix(array)) {
29 array = array.valueOf()
30 }
31
32 for (let i = 0, ii = array.length; i < ii; i++) {
33 const value = array[i]
34
35 if (Array.isArray(value)) {
36 deepForEach(value, callback)
37 } else {
38 callback(value)
39 }
40 }
41}
42
43/**
44 * Execute the callback function element wise for each element in array and any
45 * nested array
46 * Returns an array with the results
47 * @param {Array | Matrix} array
48 * @param {Function} callback The callback is called with two parameters:
49 * value1 and value2, which contain the current
50 * element of both arrays.
51 * @param {boolean} [skipZeros] Invoke callback function for non-zero values only.
52 *
53 * @return {Array | Matrix} res
54 */
55export function deepMap (array, callback, skipZeros) {
56 if (array && (typeof array.map === 'function')) {
57 // TODO: replace array.map with a for loop to improve performance
58 return array.map(function (x) {
59 return deepMap(x, callback, skipZeros)
60 })
61 } else {
62 return callback(array)
63 }
64}
65
66/**
67 * Reduce a given matrix or array to a new matrix or
68 * array with one less dimension, applying the given
69 * callback in the selected dimension.
70 * @param {Array | Matrix} mat
71 * @param {number} dim
72 * @param {Function} callback
73 * @return {Array | Matrix} res
74 */
75export function reduce (mat, dim, callback) {
76 const size = Array.isArray(mat) ? arraySize(mat) : mat.size()
77 if (dim < 0 || (dim >= size.length)) {
78 // TODO: would be more clear when throwing a DimensionError here
79 throw new IndexError(dim, size.length)
80 }
81
82 if (isMatrix(mat)) {
83 return mat.create(_reduce(mat.valueOf(), dim, callback))
84 } else {
85 return _reduce(mat, dim, callback)
86 }
87}
88
89/**
90 * Recursively reduce a matrix
91 * @param {Array} mat
92 * @param {number} dim
93 * @param {Function} callback
94 * @returns {Array} ret
95 * @private
96 */
97function _reduce (mat, dim, callback) {
98 let i, ret, val, tran
99
100 if (dim <= 0) {
101 if (!Array.isArray(mat[0])) {
102 val = mat[0]
103 for (i = 1; i < mat.length; i++) {
104 val = callback(val, mat[i])
105 }
106 return val
107 } else {
108 tran = _switch(mat)
109 ret = []
110 for (i = 0; i < tran.length; i++) {
111 ret[i] = _reduce(tran[i], dim - 1, callback)
112 }
113 return ret
114 }
115 } else {
116 ret = []
117 for (i = 0; i < mat.length; i++) {
118 ret[i] = _reduce(mat[i], dim - 1, callback)
119 }
120 return ret
121 }
122}
123
124/**
125 * Transpose a matrix
126 * @param {Array} mat
127 * @returns {Array} ret
128 * @private
129 */
130function _switch (mat) {
131 const I = mat.length
132 const J = mat[0].length
133 let i, j
134 const ret = []
135 for (j = 0; j < J; j++) {
136 const tmp = []
137 for (i = 0; i < I; i++) {
138 tmp.push(mat[i][j])
139 }
140 ret.push(tmp)
141 }
142 return ret
143}
144
145// TODO: document function scatter
146export function scatter (a, j, w, x, u, mark, c, f, inverse, update, value) {
147 // a arrays
148 const avalues = a._values
149 const aindex = a._index
150 const aptr = a._ptr
151 // c arrays
152 const cindex = c._index
153
154 // vars
155 let k, k0, k1, i
156
157 // check we need to process values (pattern matrix)
158 if (x) {
159 // values in j
160 for (k0 = aptr[j], k1 = aptr[j + 1], k = k0; k < k1; k++) {
161 // row
162 i = aindex[k]
163 // check value exists in current j
164 if (w[i] !== mark) {
165 // i is new entry in j
166 w[i] = mark
167 // add i to pattern of C
168 cindex.push(i)
169 // x(i) = A, check we need to call function this time
170 if (update) {
171 // copy value to workspace calling callback function
172 x[i] = inverse ? f(avalues[k], value) : f(value, avalues[k])
173 // function was called on current row
174 u[i] = mark
175 } else {
176 // copy value to workspace
177 x[i] = avalues[k]
178 }
179 } else {
180 // i exists in C already
181 x[i] = inverse ? f(avalues[k], x[i]) : f(x[i], avalues[k])
182 // function was called on current row
183 u[i] = mark
184 }
185 }
186 } else {
187 // values in j
188 for (k0 = aptr[j], k1 = aptr[j + 1], k = k0; k < k1; k++) {
189 // row
190 i = aindex[k]
191 // check value exists in current j
192 if (w[i] !== mark) {
193 // i is new entry in j
194 w[i] = mark
195 // add i to pattern of C
196 cindex.push(i)
197 } else {
198 // indicate function was called on current row
199 u[i] = mark
200 }
201 }
202 }
203}