UNPKG

@etsoo/shared

Version:

TypeScript shared utilities and functions

216 lines (187 loc) 5.39 kB
import isEqual from "lodash.isequal"; import { DataTypes } from "./DataTypes"; declare global { interface Array<T> { /** * Items do not exist in target array or reverse match * @param target Target array * @param round A round for both matches */ different(target: Array<T>, round?: boolean): Array<T>; /** * Get max number item or number item property * @param field Object field to calculate */ max( ...field: T extends number ? [undefined?] : T extends object ? [DataTypes.Keys<T, number>] : [never] ): number; /** * Get max field value item * @param field Object field to calculate */ maxItem( field: T extends object ? DataTypes.Keys<T, number> : never ): T | undefined; /** * Get min number item or number item property * @param field Object field to calculate */ min( ...field: T extends number ? [undefined?] : T extends object ? [DataTypes.Keys<T, number>] : [never] ): number; /** * Get min field value item * @param field Object field to calculate */ minItem( field: T extends object ? DataTypes.Keys<T, number> : never ): T | undefined; /** * Remove items by value or condition * @param items Items to remove */ remove( ...items: ((T & (DataTypes.Basic | object)) | ((item: T) => boolean))[] ): T[]; /** * Sort by property * @param property Property * @param values Property values */ sortByProperty<P extends keyof T>(property: P, values: T[P][]): T[]; /** * Sum number items or number item properties * @param field Object field to calculate */ sum( ...field: T extends number ? [undefined?] : T extends object ? [DataTypes.Keys<T, number>] : [never] ): number; /** * Make all items are unique * @param this Input array */ toUnique(): Array<T>; } } Array.prototype.different = function <T>( this: Array<T>, target: Array<T>, round?: boolean ) { return ArrayUtils.differences(this, target, round); }; Array.prototype.toUnique = function <T>(this: Array<T>) { if (this.length === 0 || typeof this[0] !== "object") return Array.from(new Set(this)); const newArray: T[] = []; this.forEach((item) => { if (newArray.some((newItem) => isEqual(item, newItem))) return; newArray.push(item); }); return newArray; }; Array.prototype.max = function <T>( this: Array<T>, field: T extends object ? DataTypes.Keys<T, number> : undefined ) { if (field == null) { return Math.max(...(this as Array<number>)); } return Math.max(...this.map((item) => item[field] as number)); }; Array.prototype.maxItem = function <T>( this: Array<T>, field: T extends object ? DataTypes.Keys<T, number> : never ) { if (this.length === 0) return undefined; return this.reduce((prev, curr) => (prev[field] > curr[field] ? prev : curr)); }; Array.prototype.min = function <T>( this: Array<T>, field: T extends object ? DataTypes.Keys<T, number> : undefined ) { if (field == null) { return Math.min(...(this as Array<number>)); } return Math.min(...this.map((item) => item[field] as number)); }; Array.prototype.minItem = function <T>( this: Array<T>, field: T extends object ? DataTypes.Keys<T, number> : never ) { if (this.length === 0) return undefined; return this.reduce((prev, curr) => (prev[field] < curr[field] ? prev : curr)); }; Array.prototype.remove = function <T>( this: Array<T>, ...items: ((T & (DataTypes.Basic | object)) | ((item: T) => boolean))[] ) { const funs: ((item: T) => boolean)[] = []; const results: T[] = []; items.forEach((item) => { if (typeof item === "function") { funs.push(item); } else { // For object items, should be removed by reference, not by value const index = this.indexOf(item); if (index >= 0) results.push(...this.splice(index, 1)); } }); if (funs.length > 0) { // Reduce check loops for performance for (let i = this.length - 1; i >= 0; i--) { if (funs.some((fun) => fun(this[i]))) results.push(...this.splice(i, 1)); } } return results; }; Array.prototype.sortByProperty = function <T, P extends keyof T>( this: Array<T>, property: P, values: T[P][] ) { return this.sort((a, b) => { const ai = values.indexOf(a[property]); const bi = values.indexOf(b[property]); if (ai === bi) return 0; if (ai < 0 || bi < 0) return bi === 0 ? 1 : bi; return ai - bi; }); }; Array.prototype.sum = function <T>( this: Array<T>, field: T extends object ? DataTypes.Keys<T, number> : undefined ) { if (field == null) { return this.reduce((total, num) => total + (num as number), 0); } return this.reduce((total, item) => total + (item[field] as number), 0); }; /** * Array Utilities */ export namespace ArrayUtils { /** * Array 1 items do not exist in Array 2 or reverse match * @param a1 Array 1 * @param a2 Array 2 * @param round A round for both matches */ export function differences<T>(a1: T[], a2: T[], round?: boolean) { const diff = a1.filter((x) => !a2.includes(x)); if (round) return [...diff, ...a2.filter((x) => !a1.includes(x))]; return diff; } }