UNPKG

15.6 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.arraySize = arraySize;
7exports.validate = validate;
8exports.validateIndex = validateIndex;
9exports.resize = resize;
10exports.reshape = reshape;
11exports.squeeze = squeeze;
12exports.unsqueeze = unsqueeze;
13exports.flatten = flatten;
14exports.map = map;
15exports.forEach = forEach;
16exports.filter = filter;
17exports.filterRegExp = filterRegExp;
18exports.join = join;
19exports.identify = identify;
20exports.generalize = generalize;
21exports.getArrayDataType = getArrayDataType;
22exports.last = last;
23exports.initial = initial;
24exports.contains = contains;
25
26var _number = require("./number");
27
28var _is = require("./is");
29
30var _string = require("./string");
31
32var _DimensionError = require("../error/DimensionError");
33
34var _IndexError = require("../error/IndexError");
35
36/**
37 * Calculate the size of a multi dimensional array.
38 * This function checks the size of the first entry, it does not validate
39 * whether all dimensions match. (use function `validate` for that)
40 * @param {Array} x
41 * @Return {Number[]} size
42 */
43function arraySize(x) {
44 var s = [];
45
46 while (Array.isArray(x)) {
47 s.push(x.length);
48 x = x[0];
49 }
50
51 return s;
52}
53/**
54 * Recursively validate whether each element in a multi dimensional array
55 * has a size corresponding to the provided size array.
56 * @param {Array} array Array to be validated
57 * @param {number[]} size Array with the size of each dimension
58 * @param {number} dim Current dimension
59 * @throws DimensionError
60 * @private
61 */
62
63
64function _validate(array, size, dim) {
65 var i;
66 var len = array.length;
67
68 if (len !== size[dim]) {
69 throw new _DimensionError.DimensionError(len, size[dim]);
70 }
71
72 if (dim < size.length - 1) {
73 // recursively validate each child array
74 var dimNext = dim + 1;
75
76 for (i = 0; i < len; i++) {
77 var child = array[i];
78
79 if (!Array.isArray(child)) {
80 throw new _DimensionError.DimensionError(size.length - 1, size.length, '<');
81 }
82
83 _validate(array[i], size, dimNext);
84 }
85 } else {
86 // last dimension. none of the childs may be an array
87 for (i = 0; i < len; i++) {
88 if (Array.isArray(array[i])) {
89 throw new _DimensionError.DimensionError(size.length + 1, size.length, '>');
90 }
91 }
92 }
93}
94/**
95 * Validate whether each element in a multi dimensional array has
96 * a size corresponding to the provided size array.
97 * @param {Array} array Array to be validated
98 * @param {number[]} size Array with the size of each dimension
99 * @throws DimensionError
100 */
101
102
103function validate(array, size) {
104 var isScalar = size.length === 0;
105
106 if (isScalar) {
107 // scalar
108 if (Array.isArray(array)) {
109 throw new _DimensionError.DimensionError(array.length, 0);
110 }
111 } else {
112 // array
113 _validate(array, size, 0);
114 }
115}
116/**
117 * Test whether index is an integer number with index >= 0 and index < length
118 * when length is provided
119 * @param {number} index Zero-based index
120 * @param {number} [length] Length of the array
121 */
122
123
124function validateIndex(index, length) {
125 if (!(0, _is.isNumber)(index) || !(0, _number.isInteger)(index)) {
126 throw new TypeError('Index must be an integer (value: ' + index + ')');
127 }
128
129 if (index < 0 || typeof length === 'number' && index >= length) {
130 throw new _IndexError.IndexError(index, length);
131 }
132}
133/**
134 * Resize a multi dimensional array. The resized array is returned.
135 * @param {Array} array Array to be resized
136 * @param {Array.<number>} size Array with the size of each dimension
137 * @param {*} [defaultValue=0] Value to be filled in in new entries,
138 * zero by default. Specify for example `null`,
139 * to clearly see entries that are not explicitly
140 * set.
141 * @return {Array} array The resized array
142 */
143
144
145function resize(array, size, defaultValue) {
146 // TODO: add support for scalars, having size=[] ?
147 // check the type of the arguments
148 if (!Array.isArray(array) || !Array.isArray(size)) {
149 throw new TypeError('Array expected');
150 }
151
152 if (size.length === 0) {
153 throw new Error('Resizing to scalar is not supported');
154 } // check whether size contains positive integers
155
156
157 size.forEach(function (value) {
158 if (!(0, _is.isNumber)(value) || !(0, _number.isInteger)(value) || value < 0) {
159 throw new TypeError('Invalid size, must contain positive integers ' + '(size: ' + (0, _string.format)(size) + ')');
160 }
161 }); // recursively resize the array
162
163 var _defaultValue = defaultValue !== undefined ? defaultValue : 0;
164
165 _resize(array, size, 0, _defaultValue);
166
167 return array;
168}
169/**
170 * Recursively resize a multi dimensional array
171 * @param {Array} array Array to be resized
172 * @param {number[]} size Array with the size of each dimension
173 * @param {number} dim Current dimension
174 * @param {*} [defaultValue] Value to be filled in in new entries,
175 * undefined by default.
176 * @private
177 */
178
179
180function _resize(array, size, dim, defaultValue) {
181 var i;
182 var elem;
183 var oldLen = array.length;
184 var newLen = size[dim];
185 var minLen = Math.min(oldLen, newLen); // apply new length
186
187 array.length = newLen;
188
189 if (dim < size.length - 1) {
190 // non-last dimension
191 var dimNext = dim + 1; // resize existing child arrays
192
193 for (i = 0; i < minLen; i++) {
194 // resize child array
195 elem = array[i];
196
197 if (!Array.isArray(elem)) {
198 elem = [elem]; // add a dimension
199
200 array[i] = elem;
201 }
202
203 _resize(elem, size, dimNext, defaultValue);
204 } // create new child arrays
205
206
207 for (i = minLen; i < newLen; i++) {
208 // get child array
209 elem = [];
210 array[i] = elem; // resize new child array
211
212 _resize(elem, size, dimNext, defaultValue);
213 }
214 } else {
215 // last dimension
216 // remove dimensions of existing values
217 for (i = 0; i < minLen; i++) {
218 while (Array.isArray(array[i])) {
219 array[i] = array[i][0];
220 }
221 } // fill new elements with the default value
222
223
224 for (i = minLen; i < newLen; i++) {
225 array[i] = defaultValue;
226 }
227 }
228}
229/**
230 * Re-shape a multi dimensional array to fit the specified dimensions
231 * @param {Array} array Array to be reshaped
232 * @param {Array.<number>} sizes List of sizes for each dimension
233 * @returns {Array} Array whose data has been formatted to fit the
234 * specified dimensions
235 *
236 * @throws {DimensionError} If the product of the new dimension sizes does
237 * not equal that of the old ones
238 */
239
240
241function reshape(array, sizes) {
242 var flatArray = flatten(array);
243 var newArray;
244
245 function product(arr) {
246 return arr.reduce(function (prev, curr) {
247 return prev * curr;
248 });
249 }
250
251 if (!Array.isArray(array) || !Array.isArray(sizes)) {
252 throw new TypeError('Array expected');
253 }
254
255 if (sizes.length === 0) {
256 throw new _DimensionError.DimensionError(0, product(arraySize(array)), '!=');
257 }
258
259 var totalSize = 1;
260
261 for (var sizeIndex = 0; sizeIndex < sizes.length; sizeIndex++) {
262 totalSize *= sizes[sizeIndex];
263 }
264
265 if (flatArray.length !== totalSize) {
266 throw new _DimensionError.DimensionError(product(sizes), product(arraySize(array)), '!=');
267 }
268
269 try {
270 newArray = _reshape(flatArray, sizes);
271 } catch (e) {
272 if (e instanceof _DimensionError.DimensionError) {
273 throw new _DimensionError.DimensionError(product(sizes), product(arraySize(array)), '!=');
274 }
275
276 throw e;
277 }
278
279 return newArray;
280}
281/**
282 * Iteratively re-shape a multi dimensional array to fit the specified dimensions
283 * @param {Array} array Array to be reshaped
284 * @param {Array.<number>} sizes List of sizes for each dimension
285 * @returns {Array} Array whose data has been formatted to fit the
286 * specified dimensions
287 */
288
289
290function _reshape(array, sizes) {
291 // testing if there are enough elements for the requested shape
292 var tmpArray = array;
293 var tmpArray2; // for each dimensions starting by the last one and ignoring the first one
294
295 for (var sizeIndex = sizes.length - 1; sizeIndex > 0; sizeIndex--) {
296 var size = sizes[sizeIndex];
297 tmpArray2 = []; // aggregate the elements of the current tmpArray in elements of the requested size
298
299 var length = tmpArray.length / size;
300
301 for (var i = 0; i < length; i++) {
302 tmpArray2.push(tmpArray.slice(i * size, (i + 1) * size));
303 } // set it as the new tmpArray for the next loop turn or for return
304
305
306 tmpArray = tmpArray2;
307 }
308
309 return tmpArray;
310}
311/**
312 * Squeeze a multi dimensional array
313 * @param {Array} array
314 * @param {Array} [size]
315 * @returns {Array} returns the array itself
316 */
317
318
319function squeeze(array, size) {
320 var s = size || arraySize(array); // squeeze outer dimensions
321
322 while (Array.isArray(array) && array.length === 1) {
323 array = array[0];
324 s.shift();
325 } // find the first dimension to be squeezed
326
327
328 var dims = s.length;
329
330 while (s[dims - 1] === 1) {
331 dims--;
332 } // squeeze inner dimensions
333
334
335 if (dims < s.length) {
336 array = _squeeze(array, dims, 0);
337 s.length = dims;
338 }
339
340 return array;
341}
342/**
343 * Recursively squeeze a multi dimensional array
344 * @param {Array} array
345 * @param {number} dims Required number of dimensions
346 * @param {number} dim Current dimension
347 * @returns {Array | *} Returns the squeezed array
348 * @private
349 */
350
351
352function _squeeze(array, dims, dim) {
353 var i, ii;
354
355 if (dim < dims) {
356 var next = dim + 1;
357
358 for (i = 0, ii = array.length; i < ii; i++) {
359 array[i] = _squeeze(array[i], dims, next);
360 }
361 } else {
362 while (Array.isArray(array)) {
363 array = array[0];
364 }
365 }
366
367 return array;
368}
369/**
370 * Unsqueeze a multi dimensional array: add dimensions when missing
371 *
372 * Paramter `size` will be mutated to match the new, unqueezed matrix size.
373 *
374 * @param {Array} array
375 * @param {number} dims Desired number of dimensions of the array
376 * @param {number} [outer] Number of outer dimensions to be added
377 * @param {Array} [size] Current size of array.
378 * @returns {Array} returns the array itself
379 * @private
380 */
381
382
383function unsqueeze(array, dims, outer, size) {
384 var s = size || arraySize(array); // unsqueeze outer dimensions
385
386 if (outer) {
387 for (var i = 0; i < outer; i++) {
388 array = [array];
389 s.unshift(1);
390 }
391 } // unsqueeze inner dimensions
392
393
394 array = _unsqueeze(array, dims, 0);
395
396 while (s.length < dims) {
397 s.push(1);
398 }
399
400 return array;
401}
402/**
403 * Recursively unsqueeze a multi dimensional array
404 * @param {Array} array
405 * @param {number} dims Required number of dimensions
406 * @param {number} dim Current dimension
407 * @returns {Array | *} Returns the squeezed array
408 * @private
409 */
410
411
412function _unsqueeze(array, dims, dim) {
413 var i, ii;
414
415 if (Array.isArray(array)) {
416 var next = dim + 1;
417
418 for (i = 0, ii = array.length; i < ii; i++) {
419 array[i] = _unsqueeze(array[i], dims, next);
420 }
421 } else {
422 for (var d = dim; d < dims; d++) {
423 array = [array];
424 }
425 }
426
427 return array;
428}
429/**
430 * Flatten a multi dimensional array, put all elements in a one dimensional
431 * array
432 * @param {Array} array A multi dimensional array
433 * @return {Array} The flattened array (1 dimensional)
434 */
435
436
437function flatten(array) {
438 if (!Array.isArray(array)) {
439 // if not an array, return as is
440 return array;
441 }
442
443 var flat = [];
444 array.forEach(function callback(value) {
445 if (Array.isArray(value)) {
446 value.forEach(callback); // traverse through sub-arrays recursively
447 } else {
448 flat.push(value);
449 }
450 });
451 return flat;
452}
453/**
454 * A safe map
455 * @param {Array} array
456 * @param {function} callback
457 */
458
459
460function map(array, callback) {
461 return Array.prototype.map.call(array, callback);
462}
463/**
464 * A safe forEach
465 * @param {Array} array
466 * @param {function} callback
467 */
468
469
470function forEach(array, callback) {
471 Array.prototype.forEach.call(array, callback);
472}
473/**
474 * A safe filter
475 * @param {Array} array
476 * @param {function} callback
477 */
478
479
480function filter(array, callback) {
481 if (arraySize(array).length !== 1) {
482 throw new Error('Only one dimensional matrices supported');
483 }
484
485 return Array.prototype.filter.call(array, callback);
486}
487/**
488 * Filter values in a callback given a regular expression
489 * @param {Array} array
490 * @param {RegExp} regexp
491 * @return {Array} Returns the filtered array
492 * @private
493 */
494
495
496function filterRegExp(array, regexp) {
497 if (arraySize(array).length !== 1) {
498 throw new Error('Only one dimensional matrices supported');
499 }
500
501 return Array.prototype.filter.call(array, function (entry) {
502 return regexp.test(entry);
503 });
504}
505/**
506 * A safe join
507 * @param {Array} array
508 * @param {string} separator
509 */
510
511
512function join(array, separator) {
513 return Array.prototype.join.call(array, separator);
514}
515/**
516 * Assign a numeric identifier to every element of a sorted array
517 * @param {Array} a An array
518 * @return {Array} An array of objects containing the original value and its identifier
519 */
520
521
522function identify(a) {
523 if (!Array.isArray(a)) {
524 throw new TypeError('Array input expected');
525 }
526
527 if (a.length === 0) {
528 return a;
529 }
530
531 var b = [];
532 var count = 0;
533 b[0] = {
534 value: a[0],
535 identifier: 0
536 };
537
538 for (var i = 1; i < a.length; i++) {
539 if (a[i] === a[i - 1]) {
540 count++;
541 } else {
542 count = 0;
543 }
544
545 b.push({
546 value: a[i],
547 identifier: count
548 });
549 }
550
551 return b;
552}
553/**
554 * Remove the numeric identifier from the elements
555 * @param {array} a An array
556 * @return {array} An array of values without identifiers
557 */
558
559
560function generalize(a) {
561 if (!Array.isArray(a)) {
562 throw new TypeError('Array input expected');
563 }
564
565 if (a.length === 0) {
566 return a;
567 }
568
569 var b = [];
570
571 for (var i = 0; i < a.length; i++) {
572 b.push(a[i].value);
573 }
574
575 return b;
576}
577/**
578 * Check the datatype of a given object
579 * This is a low level implementation that should only be used by
580 * parent Matrix classes such as SparseMatrix or DenseMatrix
581 * This method does not validate Array Matrix shape
582 * @param {Array} array
583 * @param {function} typeOf Callback function to use to determine the type of a value
584 * @return string
585 */
586
587
588function getArrayDataType(array, typeOf) {
589 var type; // to hold type info
590
591 var length = 0; // to hold length value to ensure it has consistent sizes
592
593 for (var i = 0; i < array.length; i++) {
594 var item = array[i];
595 var isArray = Array.isArray(item); // Saving the target matrix row size
596
597 if (i === 0 && isArray) {
598 length = item.length;
599 } // If the current item is an array but the length does not equal the targetVectorSize
600
601
602 if (isArray && item.length !== length) {
603 return undefined;
604 }
605
606 var itemType = isArray ? getArrayDataType(item, typeOf) // recurse into a nested array
607 : typeOf(item);
608
609 if (type === undefined) {
610 type = itemType; // first item
611 } else if (type !== itemType) {
612 return 'mixed';
613 } else {// we're good, everything has the same type so far
614 }
615 }
616
617 return type;
618}
619/**
620 * Return the last item from an array
621 * @param array
622 * @returns {*}
623 */
624
625
626function last(array) {
627 return array[array.length - 1];
628}
629/**
630 * Get all but the last element of array.
631 */
632
633
634function initial(array) {
635 return array.slice(0, array.length - 1);
636}
637/**
638 * Test whether an array or string contains an item
639 * @param {Array | string} array
640 * @param {*} item
641 * @return {boolean}
642 */
643
644
645function contains(array, item) {
646 return array.indexOf(item) !== -1;
647}
\No newline at end of file