Skip to content

Commit

Permalink
feat: add array.first, edit readme, update flake
Browse files Browse the repository at this point in the history
* added first function to array to find first element matching a predicate.
* edited the table in the documentation section of README.md to be slightly more
  readable.
* ran nix flake update
  • Loading branch information
baetheus committed Nov 9, 2023
1 parent b441354 commit 5ccf5f5
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 13 deletions.
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,31 +59,31 @@ structures and more general algebraic structures.
| [Sortable](./sortable.ts) | || | Ord |
| [Traversable](./traversable.ts) | || | |
| [Wrappable](./wrappable.ts) | || | Pointed |
| [Newtype](./newtype.ts) | | | | Brand, Branded Type |
| [AsyncIterable](./async_iterable.ts) || || |
| [Boolean](./boolean.ts) || || |
| [Iterable](./iterable.ts) || || |
| [Number](./number.ts) || || |
| [Promise](./promise.ts) || || |
| [ReadonlyArray](./array.ts) || || Array |
| [ReadonlyMap](./map.ts) || || Map |
| [ReadonlySet](./set.ts) || || Set |
| [String](./string.ts) || || |
| [Async](./async.ts) || | | Task |
| [AsyncEither](./async_either.ts) || | | TaskEither |
| [AsyncIterable](./async_iterable.ts) || || |
| [Boolean](./boolean.ts) || || |
| [Decoder](./decoder.ts) || | | |
| [Either](./either.ts) || | | |
| [Fn](./fn.ts) || | | Reader |
| [Fn](./fn.ts) || | | Reader |
| [FnEither](./fn_either.ts) || | | ReaderEither |
| [Identity](./identity.ts) || | | Trivial |
| [Iterable](./iterable.ts) || || |
| [JsonSchema](./json_schema.ts) || | | |
| [ReadonlyMap](./map.ts) || || Map |
| [Newtype](./newtype.ts) | | | | Brand, Branded Type |
| [Nilable](./nilable.ts) || | | |
| [Number](./number.ts) || || |
| [Optic](./optic.ts) || | | Iso, Lens, Optional, Prism, Traversal |
| [Option](./option.ts) || | | Maybe |
| [Pair](./pair.ts) || | | Separated |
| [Predicate](./predicate.ts) || | | |
| [Promise](./promise.ts) || || |
| [Refinement](./refinement.ts) || | | |
| [ReadonlySet](./set.ts) || || Set |
| [State](./state.ts) || | | |
| [String](./string.ts) || || |
| [Sync](./sync.ts) || | | IO |
| [SyncEither](./sync_either.ts) || | | IOEither |
| [These](./these.ts) || | | |
Expand Down
44 changes: 44 additions & 0 deletions array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1282,6 +1282,50 @@ export function zip<A extends ReadonlyArray<AnyArray>>(
}
}

/**
* Find a value in an array using a predicate or refinement, optionally skipping
* some number of values. This find function returns early at the first found
* value after the supplied number of skips.
*
* @example
* ```ts
* import * as A from "./array.ts";
*
* const positive = A.first((n: number) => n > 0);
*
* const result1 = positive(A.range(3, -1)); // Some(1)
* const result2 = positive(A.range(3, -2)); // None
* ```
*
* @since 2.0.0
*/
export function first<A>(
predicate: (a: A, index: number) => boolean,
skip?: number,
): (ua: ReadonlyArray<A>) => Option<A>;
export function first<A, B extends A>(
predicate: (a: A, index: number) => a is B,
skip?: number,
): (ua: ReadonlyArray<A>) => Option<B>;
export function first<A>(
predicate: (a: A, index: number) => boolean,
skip = 0,
): (ua: ReadonlyArray<A>) => Option<A> {
const _skip = Math.max(0, Math.floor(skip));
return (ua) => {
const length = ua.length;
let index = -1;
let found = 0;
while (++index < length) {
const value = ua[index];
if (predicate(value, index) && ++found > _skip) {
return some(value);
}
}
return none;
};
}

/**
* @since 2.0.0
*/
Expand Down
6 changes: 3 additions & 3 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions testing/array.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,14 @@ Deno.test("Array zip", () => {
]]);
});

Deno.test("Array first", () => {
assertEquals(pipe(A.range(0), A.first((n) => n > 0)), O.none);
assertEquals(pipe(A.range(1), A.first((n) => n === 0)), O.some(0));
assertEquals(pipe(A.range(2), A.first((n) => n > 0)), O.some(1));
assertEquals(pipe(A.range(3), A.first((n) => n > 0, 1)), O.some(2));
assertEquals(pipe(A.range(4), A.first((n) => n > 10)), O.none);
});

Deno.test("Array range", () => {
assertEquals(A.range(0), []);
assertEquals(A.range(1), [0]);
Expand Down
18 changes: 18 additions & 0 deletions testing/failable.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,21 @@ Deno.test("Failable createTryAll", () => {
E.left(3),
);
});

Deno.test("Failable createTap", () => {
const tap = F.createTap(E.FailableEither);

let success: number | undefined;
let failure: number | undefined;

const _tap = tap((s: number) => {
success = s;
}, (f: number) => {
failure = f;
});

_tap(E.right(1));
_tap(E.left(2));

assertEquals([success, failure], [1, 2]);
});

0 comments on commit 5ccf5f5

Please sign in to comment.