this repo has no description
1import type { Option } from './option'
2
3export function first<T>(arr: readonly T[]): Option<T> {
4 return arr[0] ?? null
5}
6
7export function last<T>(arr: readonly T[]): Option<T> {
8 return arr[arr.length - 1] ?? null
9}
10
11export function at<T>(arr: readonly T[], index: number): Option<T> {
12 if (index < 0) index = arr.length + index
13 return arr[index] ?? null
14}
15
16export function find<T>(arr: readonly T[], predicate: (t: T) => boolean): Option<T> {
17 return arr.find(predicate) ?? null
18}
19
20export function findMap<T, U>(arr: readonly T[], fn: (t: T) => Option<U>): Option<U> {
21 for (const item of arr) {
22 const result = fn(item)
23 if (result != null) return result
24 }
25 return null
26}
27
28export function findIndex<T>(arr: readonly T[], predicate: (t: T) => boolean): Option<number> {
29 const index = arr.findIndex(predicate)
30 return index >= 0 ? index : null
31}
32
33export function partition<T>(
34 arr: readonly T[],
35 predicate: (t: T) => boolean
36): [T[], T[]] {
37 const pass: T[] = []
38 const fail: T[] = []
39 for (const item of arr) {
40 if (predicate(item)) {
41 pass.push(item)
42 } else {
43 fail.push(item)
44 }
45 }
46 return [pass, fail]
47}
48
49export function groupBy<T, K extends string | number>(
50 arr: readonly T[],
51 keyFn: (t: T) => K
52): Record<K, T[]> {
53 const result = {} as Record<K, T[]>
54 for (const item of arr) {
55 const key = keyFn(item)
56 if (!result[key]) {
57 result[key] = []
58 }
59 result[key].push(item)
60 }
61 return result
62}
63
64export function unique<T>(arr: readonly T[]): T[] {
65 return [...new Set(arr)]
66}
67
68export function uniqueBy<T, K>(arr: readonly T[], keyFn: (t: T) => K): T[] {
69 const seen = new Set<K>()
70 const result: T[] = []
71 for (const item of arr) {
72 const key = keyFn(item)
73 if (!seen.has(key)) {
74 seen.add(key)
75 result.push(item)
76 }
77 }
78 return result
79}
80
81export function sortBy<T>(arr: readonly T[], keyFn: (t: T) => number | string): T[] {
82 return [...arr].sort((a, b) => {
83 const ka = keyFn(a)
84 const kb = keyFn(b)
85 if (ka < kb) return -1
86 if (ka > kb) return 1
87 return 0
88 })
89}
90
91export function sortByDesc<T>(arr: readonly T[], keyFn: (t: T) => number | string): T[] {
92 return [...arr].sort((a, b) => {
93 const ka = keyFn(a)
94 const kb = keyFn(b)
95 if (ka > kb) return -1
96 if (ka < kb) return 1
97 return 0
98 })
99}
100
101export function chunk<T>(arr: readonly T[], size: number): T[][] {
102 const result: T[][] = []
103 for (let i = 0; i < arr.length; i += size) {
104 result.push(arr.slice(i, i + size))
105 }
106 return result
107}
108
109export function zip<T, U>(a: readonly T[], b: readonly U[]): [T, U][] {
110 const length = Math.min(a.length, b.length)
111 const result: [T, U][] = []
112 for (let i = 0; i < length; i++) {
113 result.push([a[i], b[i]])
114 }
115 return result
116}
117
118export function zipWith<T, U, R>(
119 a: readonly T[],
120 b: readonly U[],
121 fn: (t: T, u: U) => R
122): R[] {
123 const length = Math.min(a.length, b.length)
124 const result: R[] = []
125 for (let i = 0; i < length; i++) {
126 result.push(fn(a[i], b[i]))
127 }
128 return result
129}
130
131export function intersperse<T>(arr: readonly T[], separator: T): T[] {
132 if (arr.length <= 1) return [...arr]
133 const result: T[] = [arr[0]]
134 for (let i = 1; i < arr.length; i++) {
135 result.push(separator, arr[i])
136 }
137 return result
138}
139
140export function range(start: number, end: number): number[] {
141 const result: number[] = []
142 for (let i = start; i < end; i++) {
143 result.push(i)
144 }
145 return result
146}
147
148export function isEmpty<T>(arr: readonly T[]): boolean {
149 return arr.length === 0
150}
151
152export function isNonEmpty<T>(arr: readonly T[]): arr is [T, ...T[]] {
153 return arr.length > 0
154}
155
156export function sum(arr: readonly number[]): number {
157 return arr.reduce((acc, n) => acc + n, 0)
158}
159
160export function sumBy<T>(arr: readonly T[], fn: (t: T) => number): number {
161 return arr.reduce((acc, t) => acc + fn(t), 0)
162}
163
164export function maxBy<T>(arr: readonly T[], fn: (t: T) => number): Option<T> {
165 if (arr.length === 0) return null
166 let max = arr[0]
167 let maxValue = fn(max)
168 for (let i = 1; i < arr.length; i++) {
169 const value = fn(arr[i])
170 if (value > maxValue) {
171 max = arr[i]
172 maxValue = value
173 }
174 }
175 return max
176}
177
178export function minBy<T>(arr: readonly T[], fn: (t: T) => number): Option<T> {
179 if (arr.length === 0) return null
180 let min = arr[0]
181 let minValue = fn(min)
182 for (let i = 1; i < arr.length; i++) {
183 const value = fn(arr[i])
184 if (value < minValue) {
185 min = arr[i]
186 minValue = value
187 }
188 }
189 return min
190}