Skip to content

Commit

Permalink
Add Option-returning overloads for findFirst and findLast in Readonly… (
Browse files Browse the repository at this point in the history
  • Loading branch information
gcanti authored Jan 3, 2024
1 parent 78fec17 commit 0724211
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 38 deletions.
5 changes: 5 additions & 0 deletions .changeset/green-squids-travel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": patch
---

Add Option-returning overloads for findFirst and findLast in ReadonlyArray
86 changes: 86 additions & 0 deletions packages/effect/dtslint/ReadonlyArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,13 +292,56 @@ ReadonlyArray.findFirst(numbersOrStrings, (
_item // $ExpectType string | number
) => true)

ReadonlyArray.findFirst(numbersOrStrings, (
_item, // $ExpectType string | number
_i // $ExpectType number
) => true)

// $ExpectType Option<number>
ReadonlyArray.findFirst(numbersOrStrings, (
_item, // $ExpectType string | number
_i // $ExpectType number
): _item is number => true)

// $ExpectType Option<boolean>
ReadonlyArray.findFirst(numbersOrStrings, (
_item, // $ExpectType string | number
_i // $ExpectType number
) => Option.some(true))

pipe(
numbersOrStrings,
ReadonlyArray.findFirst((
_item // $ExpectType string | number
) => true)
)

pipe(
numbersOrStrings,
ReadonlyArray.findFirst((
_item, // $ExpectType string | number
_i // $ExpectType number
) => true)
)

// $ExpectType Option<number>
pipe(
numbersOrStrings,
ReadonlyArray.findFirst((
_item, // $ExpectType string | number
_i // $ExpectType number
): _item is number => true)
)

// $ExpectType Option<boolean>
pipe(
numbersOrStrings,
ReadonlyArray.findFirst((
_item, // $ExpectType string | number
_i // $ExpectType number
) => Option.some(true))
)

// $ExpectType Option<string | number>
ReadonlyArray.findFirst(numbersOrStrings, predicateNumbersOrStrings)

Expand All @@ -319,13 +362,56 @@ ReadonlyArray.findLast(numbersOrStrings, (
_item // $ExpectType string | number
) => true)

ReadonlyArray.findLast(numbersOrStrings, (
_item, // $ExpectType string | number
_i // $ExpectType number
) => true)

// $ExpectType Option<number>
ReadonlyArray.findLast(numbersOrStrings, (
_item, // $ExpectType string | number
_i // $ExpectType number
): _item is number => true)

// $ExpectType Option<boolean>
ReadonlyArray.findLast(numbersOrStrings, (
_item, // $ExpectType string | number
_i // $ExpectType number
) => Option.some(true))

pipe(
numbersOrStrings,
ReadonlyArray.findLast((
_item // $ExpectType string | number
) => true)
)

pipe(
numbersOrStrings,
ReadonlyArray.findLast((
_item, // $ExpectType string | number
_i // $ExpectType number
) => true)
)

// $ExpectType Option<number>
pipe(
numbersOrStrings,
ReadonlyArray.findLast((
_item, // $ExpectType string | number
_i // $ExpectType number
): _item is number => true)
)

// $ExpectType Option<boolean>
pipe(
numbersOrStrings,
ReadonlyArray.findLast((
_item, // $ExpectType string | number
_i // $ExpectType number
) => Option.some(true))
)

// $ExpectType Option<string | number>
ReadonlyArray.findLast(numbersOrStrings, predicateNumbersOrStrings)

Expand Down
72 changes: 49 additions & 23 deletions packages/effect/src/ReadonlyArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import * as readonlyArray from "./internal/readonlyArray.js"
import type { Option } from "./Option.js"
import * as O from "./Option.js"
import * as Order from "./Order.js"
import { not } from "./Predicate.js"
import { isBoolean, not } from "./Predicate.js"
import type { Predicate, Refinement } from "./Predicate.js"
import * as RR from "./ReadonlyRecord.js"
import * as Tuple from "./Tuple.js"
Expand Down Expand Up @@ -700,19 +700,32 @@ export const findLastIndex: {
* @since 2.0.0
*/
export const findFirst: {
<A, B extends A>(refinement: Refinement<A, B>): (self: Iterable<A>) => Option<B>
<B extends A, A = B>(predicate: Predicate<A>): (self: Iterable<B>) => Option<B>
<A, B extends A>(self: Iterable<A>, refinement: Refinement<A, B>): Option<B>
<A>(self: Iterable<A>, predicate: Predicate<A>): Option<A>
} = dual(2, <A>(self: Iterable<A>, predicate: Predicate<A>): Option<A> => {
const input = fromIterable(self)
for (let i = 0; i < input.length; i++) {
if (predicate(input[i])) {
return O.some(input[i])
<A, B>(f: (a: A, i: number) => Option<B>): (self: Iterable<A>) => Option<B>
<A, B extends A>(refinement: (a: A, i: number) => a is B): (self: Iterable<A>) => Option<B>
<B extends A, A = B>(predicate: (a: A, i: number) => boolean): (self: Iterable<B>) => Option<B>
<A, B>(self: Iterable<A>, f: (a: A, i: number) => Option<B>): Option<B>
<A, B extends A>(self: Iterable<A>, refinement: (a: A, i: number) => a is B): Option<B>
<A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): Option<A>
} = dual(
2,
<A>(self: Iterable<A>, f: ((a: A, i: number) => boolean) | ((a: A, i: number) => Option<A>)): Option<A> => {
let i = 0
for (const a of self) {
const o = f(a, i)
if (isBoolean(o)) {
if (o) {
return O.some(a)
}
} else {
if (O.isSome(o)) {
return o
}
}
i++
}
return O.none()
}
return O.none()
})
)

/**
* Find the last element for which a predicate holds.
Expand All @@ -721,19 +734,32 @@ export const findFirst: {
* @since 2.0.0
*/
export const findLast: {
<A, B extends A>(refinement: Refinement<A, B>): (self: Iterable<A>) => Option<B>
<B extends A, A = B>(predicate: Predicate<A>): (self: Iterable<B>) => Option<B>
<A, B extends A>(self: Iterable<A>, refinement: Refinement<A, B>): Option<B>
<A>(self: Iterable<A>, predicate: Predicate<A>): Option<A>
} = dual(2, <A>(self: Iterable<A>, predicate: Predicate<A>): Option<A> => {
const input = fromIterable(self)
for (let i = input.length - 1; i >= 0; i--) {
if (predicate(input[i])) {
return O.some(input[i])
<A, B>(f: (a: A, i: number) => Option<B>): (self: Iterable<A>) => Option<B>
<A, B extends A>(refinement: (a: A, i: number) => a is B): (self: Iterable<A>) => Option<B>
<B extends A, A = B>(predicate: (a: A, i: number) => boolean): (self: Iterable<B>) => Option<B>
<A, B>(self: Iterable<A>, f: (a: A, i: number) => Option<B>): Option<B>
<A, B extends A>(self: Iterable<A>, refinement: (a: A, i: number) => a is B): Option<B>
<A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): Option<A>
} = dual(
2,
<A>(self: Iterable<A>, f: ((a: A, i: number) => boolean) | ((a: A, i: number) => Option<A>)): Option<A> => {
const input = fromIterable(self)
for (let i = input.length - 1; i >= 0; i--) {
const a = input[i]
const o = f(a, i)
if (isBoolean(o)) {
if (o) {
return O.some(a)
}
} else {
if (O.isSome(o)) {
return o
}
}
}
return O.none()
}
return O.none()
})
)

/**
* Insert an element at the specified index, creating a new `NonEmptyArray`,
Expand Down
84 changes: 69 additions & 15 deletions packages/effect/test/ReadonlyArray.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,24 +287,78 @@ describe("ReadonlyArray", () => {
deepStrictEqual(pipe(new Set([1, 2, 3, 4]), RA.findLastIndex((n) => n % 2 === 0)), O.some(3))
})

it("findFirst", () => {
deepStrictEqual(pipe([], RA.findFirst((n) => n % 2 === 0)), O.none())
deepStrictEqual(pipe([1, 2, 3], RA.findFirst((n) => n % 2 === 0)), O.some(2))
deepStrictEqual(pipe([1, 2, 3, 4], RA.findFirst((n) => n % 2 === 0)), O.some(2))

deepStrictEqual(pipe(new Set<number>(), RA.findFirst((n) => n % 2 === 0)), O.none())
deepStrictEqual(pipe(new Set([1, 2, 3]), RA.findFirst((n) => n % 2 === 0)), O.some(2))
deepStrictEqual(pipe(new Set([1, 2, 3, 4]), RA.findFirst((n) => n % 2 === 0)), O.some(2))
describe("findFirst", () => {
it("boolean-returning overloads", () => {
deepStrictEqual(pipe([], RA.findFirst((n) => n % 2 === 0)), O.none())
deepStrictEqual(pipe([1, 2, 3], RA.findFirst((n) => n % 2 === 0)), O.some(2))
deepStrictEqual(pipe([1, 2, 3, 4], RA.findFirst((n) => n % 2 === 0)), O.some(2))

deepStrictEqual(pipe(new Set<number>(), RA.findFirst((n) => n % 2 === 0)), O.none())
deepStrictEqual(pipe(new Set([1, 2, 3]), RA.findFirst((n) => n % 2 === 0)), O.some(2))
deepStrictEqual(pipe(new Set([1, 2, 3, 4]), RA.findFirst((n) => n % 2 === 0)), O.some(2))
})

it("Option-returning overloads", () => {
deepStrictEqual(pipe([], RA.findFirst((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())), O.none())
deepStrictEqual(
pipe([1, 2, 3], RA.findFirst((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
O.some([2, 1])
)
deepStrictEqual(
pipe([1, 2, 3, 4], RA.findFirst((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
O.some([2, 1])
)

deepStrictEqual(
pipe(new Set<number>(), RA.findFirst((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
O.none()
)
deepStrictEqual(
pipe(new Set([1, 2, 3]), RA.findFirst((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
O.some([2, 1])
)
deepStrictEqual(
pipe(new Set([1, 2, 3, 4]), RA.findFirst((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
O.some([2, 1])
)
})
})

it("findLast", () => {
deepStrictEqual(pipe([], RA.findLast((n) => n % 2 === 0)), O.none())
deepStrictEqual(pipe([1, 2, 3], RA.findLast((n) => n % 2 === 0)), O.some(2))
deepStrictEqual(pipe([1, 2, 3, 4], RA.findLast((n) => n % 2 === 0)), O.some(4))
describe("findLast", () => {
it("boolean-returning overloads", () => {
deepStrictEqual(pipe([], RA.findLast((n) => n % 2 === 0)), O.none())
deepStrictEqual(pipe([1, 2, 3], RA.findLast((n) => n % 2 === 0)), O.some(2))
deepStrictEqual(pipe([1, 2, 3, 4], RA.findLast((n) => n % 2 === 0)), O.some(4))

deepStrictEqual(pipe(new Set<number>(), RA.findLast((n) => n % 2 === 0)), O.none())
deepStrictEqual(pipe(new Set([1, 2, 3]), RA.findLast((n) => n % 2 === 0)), O.some(2))
deepStrictEqual(pipe(new Set([1, 2, 3, 4]), RA.findLast((n) => n % 2 === 0)), O.some(4))
})

it("Option-returning overloads", () => {
deepStrictEqual(pipe([], RA.findLast((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())), O.none())
deepStrictEqual(
pipe([1, 2, 3], RA.findLast((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
O.some([2, 1])
)
deepStrictEqual(
pipe([1, 2, 3, 4], RA.findLast((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
O.some([4, 3])
)

deepStrictEqual(pipe(new Set<number>(), RA.findLast((n) => n % 2 === 0)), O.none())
deepStrictEqual(pipe(new Set([1, 2, 3]), RA.findLast((n) => n % 2 === 0)), O.some(2))
deepStrictEqual(pipe(new Set([1, 2, 3, 4]), RA.findLast((n) => n % 2 === 0)), O.some(4))
deepStrictEqual(
pipe(new Set<number>(), RA.findLast((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
O.none()
)
deepStrictEqual(
pipe(new Set([1, 2, 3]), RA.findLast((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
O.some([2, 1])
)
deepStrictEqual(
pipe(new Set([1, 2, 3, 4]), RA.findLast((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
O.some([4, 3])
)
})
})

it("insertAt", () => {
Expand Down

0 comments on commit 0724211

Please sign in to comment.