An educational pure functional programming library in TypeScript
2
fork

Configure Feed

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

Add data/array module with phantom-typed arrays

+78
+77
src/data/array.ts
··· 1 + import { pipe } from "../prelude/compose" 2 + import { some, none, type Option } from "../prelude/option" 3 + 4 + // Phantom property type 5 + declare const __props: unique symbol 6 + type Props<P extends string> = { [__props]?: P } 7 + 8 + /** 9 + * Tracked array with phantom properties. 10 + * Properties like "Sorted" or "NonEmpty" are tracked at type level. 11 + */ 12 + export type Arr<T, P extends string = never> = readonly T[] & Props<P> 13 + 14 + // Property markers 15 + export type Sorted = "Sorted" 16 + export type NonEmpty = "NonEmpty" 17 + 18 + // Constructor 19 + export const arr = <T>(xs: readonly T[]): Arr<T> => xs as Arr<T> 20 + 21 + // NonEmpty check - returns Option to track the property 22 + export const nonEmpty = <T, P extends string>(xs: Arr<T, P>): Option<Arr<T, P | NonEmpty>> => 23 + xs.length > 0 ? some(xs as Arr<T, P | NonEmpty>) : none 24 + 25 + // Safe accessors for NonEmpty arrays 26 + export const head = <T, P extends string>(xs: Arr<T, P | NonEmpty>): T => xs[0]! 27 + export const last = <T, P extends string>(xs: Arr<T, P | NonEmpty>): T => xs[xs.length - 1]! 28 + 29 + // Sorting - adds Sorted property 30 + export const sortNum = <P extends string>(xs: Arr<number, P>): Arr<number, P | Sorted> => 31 + [...xs].sort((a, b) => a - b) as Arr<number, P | Sorted> 32 + 33 + export const sortBy = <T, P extends string>(key: (t: T) => number) => 34 + (xs: Arr<T, P>): Arr<T, P | Sorted> => 35 + [...xs].sort((a, b) => key(a) - key(b)) as Arr<T, P | Sorted> 36 + 37 + // Transformations - preserve properties where valid 38 + export const map = <T, U>(f: (t: T) => U) => 39 + <P extends string>(xs: Arr<T, P>): Arr<U, Exclude<P, Sorted>> => 40 + xs.map(f) as Arr<U, Exclude<P, Sorted>> 41 + 42 + export const filter = <T>(pred: (t: T) => boolean) => 43 + <P extends string>(xs: Arr<T, P>): Arr<T, Exclude<P, NonEmpty>> => 44 + xs.filter(pred) as Arr<T, Exclude<P, NonEmpty>> 45 + 46 + export const reduce = <T, U>(f: (acc: U, t: T) => U, initial: U) => 47 + <P extends string>(xs: Arr<T, P>): U => 48 + xs.reduce(f, initial) 49 + 50 + // Take/drop 51 + export const take = (n: number) => 52 + <T, P extends string>(xs: Arr<T, P>): Arr<T, Exclude<P, NonEmpty>> => 53 + xs.slice(0, n) as Arr<T, Exclude<P, NonEmpty>> 54 + 55 + export const drop = (n: number) => 56 + <T, P extends string>(xs: Arr<T, P>): Arr<T, Exclude<P, NonEmpty>> => 57 + xs.slice(n) as Arr<T, Exclude<P, NonEmpty>> 58 + 59 + // Binary search - requires Sorted, pure recursive implementation 60 + export const binarySearchNum = (target: number) => 61 + <P extends string>(xs: Arr<number, P | Sorted>): Option<number> => { 62 + const go = (lo: number, hi: number): Option<number> => 63 + lo > hi 64 + ? none 65 + : pipe( 66 + Math.floor((lo + hi) / 2), 67 + mid => { 68 + const cmp = target - xs[mid]! 69 + return cmp === 0 70 + ? some(mid) 71 + : cmp < 0 72 + ? go(lo, mid - 1) 73 + : go(mid + 1, hi) 74 + } 75 + ) 76 + return go(0, xs.length - 1) 77 + }
+1
src/data/index.ts
··· 5 5 */ 6 6 7 7 export * from "./guards" 8 + export * from "./array"