Skip to content

Commit 8ad0b91

Browse files
committed
feat: add all function to combine multiple Result values
1 parent e26576f commit 8ad0b91

File tree

3 files changed

+78
-0
lines changed

3 files changed

+78
-0
lines changed

docs/result/index.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,3 +416,31 @@ console.log(result2); // false
416416
```
417417

418418
---
419+
420+
### `all`
421+
422+
Combines an array of `Result` values into a single `Result`. Returns `Ok` with all values if all are `Ok`, or `Err` with all errors if any are `Err`.
423+
424+
```ts
425+
import { all, ok, err } from 'holo-fn/result';
426+
427+
// All success case
428+
const result1 = all([ok(1), ok(2), ok(3)]);
429+
console.log(result1.unwrapOr([])); // [1, 2, 3]
430+
431+
// Collecting errors
432+
const result2 = all([err('Name required'), err('Email invalid'), ok(25)]);
433+
console.log(
434+
result2.match({
435+
ok: (v) => v,
436+
err: (e) => e,
437+
})
438+
); // ['Name required', 'Email invalid']
439+
440+
// Empty array
441+
const result3 = all([]);
442+
console.log(result3.unwrapOr([])); // []
443+
```
444+
445+
---
446+

src/result/index.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,5 +175,24 @@ export const equals =
175175
return result.equals(other);
176176
};
177177

178+
export const all = <T, E>(results: Result<T, E>[]): Result<T[], E[]> => {
179+
const values: T[] = [];
180+
const errors: E[] = [];
181+
182+
for (const result of results) {
183+
if (result.isErr()) {
184+
errors.push(result.extract() as E);
185+
} else {
186+
values.push(result.extract() as T);
187+
}
188+
}
189+
190+
if (errors.length > 0) {
191+
return new Err<T[], E[]>(errors);
192+
}
193+
194+
return new Ok<T[], E[]>(values);
195+
};
196+
178197
export const ok = <T, E>(value: T): Result<T, E> => new Ok(value);
179198
export const err = <T, E>(error: E): Result<T, E> => new Err(error);

tests/result.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, expect, it } from 'bun:test';
22
import { pipe } from 'rambda';
33
import {
4+
all,
45
chain,
56
equals,
67
Err,
@@ -361,4 +362,34 @@ describe('Result - Curried Helpers', () => {
361362
const result = pipe(new Err('Error'), equals(new Err('Different Error')));
362363
expect(result).toBe(false);
363364
});
365+
366+
it('all should return Ok with all values when all are Ok', () => {
367+
const results = [ok<number, string>(1), ok<number, string>(2), ok<number, string>(3)];
368+
const result = all(results);
369+
expect(result.isOk()).toBe(true);
370+
expect(result.unwrapOr([])).toEqual([1, 2, 3]);
371+
});
372+
373+
it('all should return Err with all errors when any are Err', () => {
374+
const results = [ok<number, string>(1), err<number, string>('error1'), err<number, string>('error2')];
375+
const result = all(results);
376+
expect(result.isErr()).toBe(true);
377+
expect(result.match({ ok: (_) => [], err: (e) => e })).toEqual(['error1', 'error2']);
378+
});
379+
380+
it('all should return Ok with empty array for empty input', () => {
381+
const result = all<number, string>([]);
382+
expect(result.isOk()).toBe(true);
383+
expect(result.unwrapOr([])).toEqual([]);
384+
});
385+
386+
it('all should collect all errors from multiple Err', () => {
387+
const results = [
388+
err<number, string>('Name required'),
389+
err<number, string>('Email invalid'),
390+
err<number, string>('Age too low'),
391+
];
392+
const result = all(results);
393+
expect(result.match({ ok: (_) => [], err: (e) => e })).toEqual(['Name required', 'Email invalid', 'Age too low']);
394+
});
364395
});

0 commit comments

Comments
 (0)