UNPKG

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