Skip to content

Commit eeb4d37

Browse files
committed
feat: add partition function to separate Either and Result values into successes and failures
1 parent e80b0ec commit eeb4d37

File tree

7 files changed

+163
-1
lines changed

7 files changed

+163
-1
lines changed

docs/changelog.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
2424
- Example:
2525
```ts
2626
sequence([ok(1), err('e1'), err('e2')]); // Err('e1') - stops at first!
27-
// vs all: Err(['e1', 'e2']) - collects all
27+
```
28+
29+
- **`partition` function for `Result` and `Either`**: Separates an array of monads into successes and failures.
30+
- Returns a plain object with two arrays (not a monad).
31+
- Always processes all items.
32+
- Example:
33+
```ts
34+
partition([ok(1), err('e1'), ok(2), err('e2')]);
35+
// { oks: [1, 2], errs: ['e1', 'e2'] }
2836
```
2937

3038
---

docs/either/index.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,3 +570,32 @@ console.log(result2.match({
570570

571571
---
572572

573+
### `partition`
574+
575+
Separates an array of `Either` values into two groups: `lefts` and `rights`. Always processes all items and returns both arrays.
576+
577+
Unlike `all` and `sequence` which return an `Either`, `partition` returns a plain object with two arrays.
578+
579+
```ts
580+
import { partition, left, right } from 'holo-fn/either';
581+
582+
const eithers = [
583+
right<string, number>(1),
584+
left('error1'),
585+
right<string, number>(2),
586+
left('error2'),
587+
right<string, number>(3),
588+
];
589+
590+
const { lefts, rights } = partition(eithers);
591+
console.log(rights); // [1, 2, 3]
592+
console.log(lefts); // ['error1', 'error2']
593+
594+
const { lefts: errors, rights: values } = partition(eithers);
595+
console.log(`✓ ${values.length} succeeded`);
596+
console.log(`✗ ${errors.length} failed`);
597+
errors.forEach((err) => console.error(err));
598+
```
599+
600+
---
601+

docs/result/index.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,3 +466,32 @@ console.log(result2.match({
466466

467467
---
468468

469+
### `partition`
470+
471+
Separates an array of `Result` values into two groups: successes (`oks`) and failures (`errs`). Always processes all items and returns both arrays.
472+
473+
Unlike `all` and `sequence` which return a `Result`, `partition` returns a plain object with two arrays.
474+
475+
```ts
476+
import { partition, ok, err } from 'holo-fn/result';
477+
478+
const results = [
479+
ok<number, string>(1),
480+
err<number, string>('error1'),
481+
ok<number, string>(2),
482+
err<number, string>('error2'),
483+
ok<number, string>(3),
484+
];
485+
486+
const { oks, errs } = partition(results);
487+
console.log(oks); // [1, 2, 3]
488+
console.log(errs); // ['error1', 'error2']
489+
490+
const { oks: succeeded, errs: failed } = partition(results);
491+
console.log(`✓ ${succeeded.length} succeeded`);
492+
console.log(`✗ ${failed.length} failed`);
493+
failed.forEach((err) => console.error(err));
494+
```
495+
496+
---
497+

src/either/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,5 +202,19 @@ export const sequence = <L, R>(eithers: Either<L, R>[]): Either<L, R[]> => {
202202
return new Right<L, R[]>(values);
203203
};
204204

205+
export const partition = <L, R>(eithers: Either<L, R>[]): { lefts: L[]; rights: R[] } => {
206+
return eithers.reduce(
207+
(acc, either) => {
208+
if (either.isLeft()) {
209+
acc.lefts.push(either.extract() as L);
210+
} else {
211+
acc.rights.push(either.extract() as R);
212+
}
213+
return acc;
214+
},
215+
{ lefts: [] as L[], rights: [] as R[] }
216+
);
217+
};
218+
205219
export const left = <L, R = never>(value: L): Either<L, R> => new Left(value);
206220
export const right = <L, R>(value: R): Either<L, R> => new Right(value);

src/result/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,5 +208,19 @@ export const sequence = <T, E>(results: Result<T, E>[]): Result<T[], E> => {
208208
return new Ok<T[], E>(values);
209209
};
210210

211+
export const partition = <T, E>(results: Result<T, E>[]): { oks: T[]; errs: E[] } => {
212+
return results.reduce(
213+
(acc, result) => {
214+
if (result.isErr()) {
215+
acc.errs.push(result.extract() as E);
216+
} else {
217+
acc.oks.push(result.extract() as T);
218+
}
219+
return acc;
220+
},
221+
{ oks: [] as T[], errs: [] as E[] }
222+
);
223+
};
224+
211225
export const ok = <T, E>(value: T): Result<T, E> => new Ok(value);
212226
export const err = <T, E>(error: E): Result<T, E> => new Err(error);

tests/either.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
map,
1212
mapLeft,
1313
match,
14+
partition,
1415
Right,
1516
right,
1617
sequence,
@@ -458,4 +459,37 @@ describe('Either - Curried Helpers', () => {
458459
const result = sequence(eithers);
459460
expect(result.match({ left: (e) => e, right: (_) => '' })).toBe('First error');
460461
});
462+
463+
it('partition should separate lefts and rights', () => {
464+
const eithers = [
465+
right<string, number>(1),
466+
left<string, number>('error1'),
467+
right<string, number>(2),
468+
left<string, number>('error2'),
469+
right<string, number>(3),
470+
];
471+
const { lefts, rights } = partition(eithers);
472+
expect(rights).toEqual([1, 2, 3]);
473+
expect(lefts).toEqual(['error1', 'error2']);
474+
});
475+
476+
it('partition should return all rights when no lefts', () => {
477+
const eithers = [right<string, number>(1), right<string, number>(2), right<string, number>(3)];
478+
const { lefts, rights } = partition(eithers);
479+
expect(rights).toEqual([1, 2, 3]);
480+
expect(lefts).toEqual([]);
481+
});
482+
483+
it('partition should return all lefts when all fail', () => {
484+
const eithers = [left<string, number>('e1'), left<string, number>('e2'), left<string, number>('e3')];
485+
const { lefts, rights } = partition(eithers);
486+
expect(lefts).toEqual(['e1', 'e2', 'e3']);
487+
expect(rights).toEqual([]);
488+
});
489+
490+
it('partition should return empty arrays for empty input', () => {
491+
const { lefts, rights } = partition<string, number>([]);
492+
expect(lefts).toEqual([]);
493+
expect(rights).toEqual([]);
494+
});
461495
});

tests/result.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
match,
1515
Ok,
1616
ok,
17+
partition,
1718
sequence,
1819
unwrapOr,
1920
validate,
@@ -419,4 +420,37 @@ describe('Result - Curried Helpers', () => {
419420
const result = sequence(results);
420421
expect(result.match({ ok: (_) => '', err: (e) => e })).toBe('First error');
421422
});
423+
424+
it('partition should separate oks and errs', () => {
425+
const results = [
426+
ok<number, string>(1),
427+
err<number, string>('error1'),
428+
ok<number, string>(2),
429+
err<number, string>('error2'),
430+
ok<number, string>(3),
431+
];
432+
const { oks, errs } = partition(results);
433+
expect(oks).toEqual([1, 2, 3]);
434+
expect(errs).toEqual(['error1', 'error2']);
435+
});
436+
437+
it('partition should return all oks when no errors', () => {
438+
const results = [ok<number, string>(1), ok<number, string>(2), ok<number, string>(3)];
439+
const { oks, errs } = partition(results);
440+
expect(oks).toEqual([1, 2, 3]);
441+
expect(errs).toEqual([]);
442+
});
443+
444+
it('partition should return all errs when all fail', () => {
445+
const results = [err<number, string>('e1'), err<number, string>('e2'), err<number, string>('e3')];
446+
const { oks, errs } = partition(results);
447+
expect(oks).toEqual([]);
448+
expect(errs).toEqual(['e1', 'e2', 'e3']);
449+
});
450+
451+
it('partition should return empty arrays for empty input', () => {
452+
const { oks, errs } = partition<number, string>([]);
453+
expect(oks).toEqual([]);
454+
expect(errs).toEqual([]);
455+
});
422456
});

0 commit comments

Comments
 (0)