/** * @author Samuel .V Sylvester Sr. * This class was originally created in November 2017 to help with developing * a machine learning application for three.js rectarea light data compression * and other applications. */ import { uniq } from "lodash"; /** * A typed array with additional utility methods. */ export class SuperArray extends Array { public readonly _isSuperArray = true; /** * Creates a new SuperArray instance. * @param {...t[]} array Elements to initialize the array. */ constructor(...array: t[]) { if (array.length === 1) { super(); // Todo, support large arrays with a loop for (const value of array) { this.push(value); } } else { super(...array); } if (array.length === 1 && (array[0] as any[])?.length) { console.warn("You passed in a single array, did you mean to spread the array instead???\n", ""); } } /** * Returns a new array with the absolute values of the old array. * @returns {SuperArray} */ public abs() { return this.map((x: any) => Math.abs(x)); } /** * Returns the average value of the elements in the array. * @returns {number} */ public avg() { return this.sum() / (this.length || 1); } /** * Returns the first element of the array. * @returns {t} */ public first() { return this[0] as t; } /** * Returns the element at the indexes location, clamped to array bounds. * @param {number} index The desired index. * @returns {t} */ public getClamped(index) { const length = this.length; if (index < 0) { return this.first(); } else if (index >= length) { return this.last(); } else { return this[index]; } } /** * Returns the index of the maximum value in the array. * @returns {number} */ public indexOfMax() { return this.indexOf(this.max() as any); } /** * Returns the last element in the array. * @returns {t} */ public last = () => { return this[this.length - 1]; }; /** * Returns the maximum value of the elements in the array. * @returns {number} */ public max = () => { let res = this[0]; for (let i = 1; i < this.length; i++) { let next = this[i]; res = res > next ? res : next; } return (res as any) as number; }; /** * Returns the minimum value of the elements in the array. * @returns {number} */ public min = () => { let res = this[0]; for (let i = 1; i < this.length; i++) { let next = this[i]; res = res < next ? res : next; } return (res as any) as number; }; public normalize() { const max = this.max(); const min = this.min(); const range = max - min || 1; return this.map(v => (+v - min) / range); } /** * Shifts the array by a given amount, optionally scaling and adding another array. * @param {number} shift Number of positions to shift. * @param {number} [multiply] Scaling factor applied after shifting. * @param {SuperArray|number[]} [addArray] Array to add element-wise after shifting. * @returns {SuperArray} */ public shiftArrayMAD(shift, multiply, addArray) { if (multiply === undefined) { multiply = 1; } if (!addArray) { addArray = [0]; } if (!addArray._isSuperArray) { addArray = new SuperArray(addArray); addArray.push(0); } return this.map((o, i) => { return (this.getClamped(i - shift) as any) * multiply + addArray.getClamped(i); }); } /** * Returns the sum of the elements in the array. * @returns {number} */ public sum = () => { return (this.reduce((a: any, b: any) => a + b) as any) as number; }; public toFixed(a) { return this.map((x: any) => +x.toFixed(a)); } /** * Returns an array based on the original with only unique values. */ public unique() { // const n = {}, // r = []; // for (var i = 0; i < this.length; i++) { // // Use a hash based on the actual data // // in the object, not the object's memory address or instance id // // This ensures that the objects are truly unique // var strRep = JSON.stringify(this[i]); // if (!n[strRep]) { // n[strRep] = true; // r.push(this[i]); // } // } return new SuperArray(...uniq(this)); } }