small array utilities
0

Configure Feed

Select the types of activity you want to include in your feed.

pkg-array-fns / lib / mod.ts
5.8 kB 215 lines
1/** 2 * returns a sequence of numbers 3 * @param start starting number (inclusive) 4 * @param end ending number (exclusive) 5 * @param step steps between numbers 6 * @returns an array containing the specified sequence of numbers 7 */ 8/*#__NO_SIDE_EFFECTS__*/ 9export const range = (start: number, end: number, step: number = 1): number[] => { 10 const len = Math.max(Math.ceil((end - start) / step), 0); 11 const result = new Array(len); 12 13 for (let i = 0; i < len; i++) { 14 result[i] = start + (i * step); 15 } 16 17 return result; 18}; 19 20/** 21 * splits an array into chunks 22 * @param array array to chunk 23 * @param size maximum amount of elements per chunk 24 * @returns array containing chunks of elements 25 */ 26/*#__NO_SIDE_EFFECTS__*/ 27export const chunked = <T>(array: T[], size: number): T[][] => { 28 const chunks: T[][] = []; 29 30 for (let i = 0, il = array.length; i < il; i += size) { 31 chunks.push(array.slice(i, i + size)); 32 } 33 34 return chunks; 35}; 36 37/** 38 * returns transformed elements, skipping undefined values 39 * @param array array to transform 40 * @param mapper function that returns either the transformed element, or undefined 41 * @returns an array of transformed elements 42 */ 43/*#__NO_SIDE_EFFECTS__*/ 44export const mapDefined = <T, R>(array: T[], mapper: (value: T) => R | undefined): R[] => { 45 const len = array.length; 46 const mapped: R[] = []; 47 48 let idx = 0; 49 let temp: R | undefined; 50 51 for (; idx < len; idx++) { 52 if ((temp = mapper(array[idx])) !== undefined) { 53 mapped.push(temp); 54 } 55 } 56 57 return mapped; 58}; 59 60/** 61 * partitions an array into two, based on whether they satisfy a given test 62 * @param array array to test 63 * @param predicate function to test each element with 64 * @returns a tuple, with the first array containing elements that satisfy the test, 65 * and the second array containing elements that doesn't satisfy the test. 66 */ 67/*#__NO_SIDE_EFFECTS__*/ 68export const partition = <T>(array: T[], predicate: (item: T) => unknown): [T[], T[]] => { 69 const a: T[] = []; 70 const b: T[] = []; 71 72 for (const item of array) { 73 (predicate(item) ? a : b).push(item); 74 } 75 76 return [a, b]; 77}; 78 79/** 80 * returns elements present in the first array but not in the second 81 * @param a the source array 82 * @param b the array to compare against 83 * @returns an array of elements in a not present in b 84 */ 85/*#__NO_SIDE_EFFECTS__*/ 86export const difference = <T>(a: T[], b: T[]): T[] => { 87 return [...new Set(a).difference(new Set(b))]; 88}; 89 90/** 91 * returns elements present in the first array but not in the second, based on a selector 92 * @param a the source array 93 * @param b the array to compare against 94 * @param selector function to derive comparison keys 95 * @returns an array of elements in a not present in b based on the selector 96 */ 97/*#__NO_SIDE_EFFECTS__*/ 98export const differenceBy = <T, K>(a: T[], b: T[], selector: (value: T) => K): T[] => { 99 const bSet = new Set(b.map(selector)); 100 const aSet = new Set<K>(); 101 102 return a.filter((value) => { 103 const key = selector(value); 104 105 if (!bSet.has(key) && !aSet.has(key)) { 106 aSet.add(key); 107 return true; 108 } 109 110 return false; 111 }); 112}; 113 114/** 115 * returns elements common to both arrays 116 * @param a the first array 117 * @param b the second array 118 * @returns an array of elements present in both a and b 119 */ 120/*#__NO_SIDE_EFFECTS__*/ 121export const intersection = <T>(a: T[], b: T[]): T[] => { 122 return [...new Set(a).intersection(new Set(b))]; 123}; 124 125/** 126 * returns an array with duplicate elements removed 127 * @param array the array to deduplicate 128 * @returns a new array with unique elements 129 */ 130/*#__NO_SIDE_EFFECTS__*/ 131export const unique = <T>(array: T[]): T[] => { 132 return [...new Set(array)]; 133}; 134 135/** 136 * returns an array with elements unique by a selector's return value 137 * @param array the array to process 138 * @param selector function to derive uniqueness keys 139 * @returns a new array with elements unique based on the selector's key 140 */ 141/*#__NO_SIDE_EFFECTS__*/ 142export const uniqueBy = <T, K>(array: T[], selector: (value: T, index: number) => K): T[] => { 143 const keys = new Set<K>(); 144 const values: T[] = []; 145 146 for (let i = 0, il = array.length; i < il; i++) { 147 const value = array[i]; 148 const key = selector(value, i); 149 150 if (keys.has(key)) { 151 continue; 152 } 153 154 keys.add(key); 155 values.push(value); 156 } 157 158 return values; 159}; 160 161/** 162 * returns a random element from the array 163 * @param arr the array to sample 164 * @returns a random element or undefined if the array is empty 165 */ 166/*#__NO_SIDE_EFFECTS__*/ 167export const sampleOne = <T>(arr: T[]): T | undefined => { 168 const len = arr.length; 169 return len !== 0 ? arr[Math.floor(Math.random() * len)] : undefined; 170}; 171 172/** 173 * selects a random subset of up to n elements from the array 174 * @param arr the array to sample from 175 * @param n the maximum number of elements to select 176 * @returns a new array containing up to n randomly selected elements 177 */ 178/*#__NO_SIDE_EFFECTS__*/ 179export const sample = <T>(arr: T[], n: number): T[] => { 180 const result = [...arr]; 181 const len = result.length; 182 183 n = Math.min(n, len); 184 185 for (let i = 0; i < n; i++) { 186 const j = i + Math.floor(Math.random() * (len - i)); 187 [result[i], result[j]] = [result[j], result[i]]; 188 } 189 190 return len > n ? result.slice(0, Math.max(0, n)) : result; 191}; 192 193/** 194 * shuffles the elements of the array 195 * @param arr the array to shuffle 196 * @returns a new array with elements in random order 197 */ 198/*#__NO_SIDE_EFFECTS__*/ 199export const shuffle = <T>(arr: T[]): T[] => { 200 return sample(arr, Infinity); 201}; 202 203/** a type of falsy values */ 204export type FalsyValue = false | null | undefined | 0 | ''; 205 206/** 207 * filters out falsy values from the array 208 * @param arr the array to filter 209 * @returns a new array with all falsy values removed 210 */ 211/*#__NO_SIDE_EFFECTS__*/ 212export const definite = <T>(arr: (T | FalsyValue)[]): T extends FalsyValue ? never[] : T[] => { 213 // deno-lint-ignore no-explicit-any 214 return arr.filter(Boolean) as any; 215};