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