Skip to content

Commit 7796f27

Browse files
vinassefranchetim-smartIMax153
authored andcommitted
feat: add Record.findFirst (#4500)
Co-authored-by: Tim <[email protected]> Co-authored-by: Maxwell Brown <[email protected]>
1 parent 26c060c commit 7796f27

File tree

4 files changed

+86
-0
lines changed

4 files changed

+86
-0
lines changed

.changeset/fast-garlics-burn.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"effect": minor
3+
---
4+
5+
Add Record.findFirst

packages/effect/dtslint/Record.tst.ts

+11
Original file line numberDiff line numberDiff line change
@@ -589,4 +589,15 @@ describe("Record", () => {
589589
expect(Record.intersection(string$structAB, { b: 2 }, (a, _) => a))
590590
.type.toBe<Record<"b", number>>()
591591
})
592+
593+
it("findFirst", () => {
594+
expect(Record.findFirst(string$numbersOrStrings, (a, _) => predicateNumbersOrStrings(a)))
595+
.type.toBe<Option.Option<[string, string | number]>>()
596+
expect(pipe(string$numbersOrStrings, Record.findFirst((a, _) => predicateNumbersOrStrings(a))))
597+
.type.toBe<Option.Option<[string, string | number]>>()
598+
expect(Record.findFirst(string$numbersOrStrings, Predicate.isString))
599+
.type.toBe<Option.Option<[string, string]>>()
600+
expect(pipe(string$numbersOrStrings, Record.findFirst(Predicate.isString)))
601+
.type.toBe<Option.Option<[string, string]>>()
602+
})
592603
})

packages/effect/src/Record.ts

+44
Original file line numberDiff line numberDiff line change
@@ -1227,3 +1227,47 @@ export const getEquivalence = <K extends string, A>(
12271227
export const singleton = <K extends string | symbol, A>(key: K, value: A): Record<K, A> => ({
12281228
[key]: value
12291229
} as any)
1230+
1231+
/**
1232+
* Returns the first entry that satisfies the specified
1233+
* predicate, or `None` if no such entry exists.
1234+
*
1235+
* @example
1236+
* ```ts
1237+
* import { Record, Option } from "effect"
1238+
*
1239+
* const record = { a: 1, b: 2, c: 3 }
1240+
* const result = Record.findFirst(record, (value, key) => value > 1 && key !== "b")
1241+
* console.log(result) // Option.Some(["c", 3])
1242+
* ```
1243+
*
1244+
* @category elements
1245+
* @since 3.14.0
1246+
*/
1247+
export const findFirst: {
1248+
<K extends string | symbol, V, V2 extends V>(
1249+
refinement: (value: NoInfer<V>, key: NoInfer<K>) => value is V2
1250+
): (self: Record<K, V>) => Option.Option<[K, V2]>
1251+
<K extends string | symbol, V>(
1252+
predicate: (value: NoInfer<V>, key: NoInfer<K>) => boolean
1253+
): (self: Record<K, V>) => Option.Option<[K, V]>
1254+
<K extends string | symbol, V, V2 extends V>(
1255+
self: Record<K, V>,
1256+
refinement: (value: NoInfer<V>, key: NoInfer<K>) => value is V2
1257+
): Option.Option<[K, V2]>
1258+
<K extends string | symbol, V>(
1259+
self: Record<K, V>,
1260+
predicate: (value: NoInfer<V>, key: NoInfer<K>) => boolean
1261+
): Option.Option<[K, V]>
1262+
} = dual(
1263+
2,
1264+
<K extends string | symbol, V>(self: Record<K, V>, f: (value: V, key: K) => boolean) => {
1265+
for (const a of Object.entries<V>(self)) {
1266+
const o = f(a[1], a[0] as K)
1267+
if (o) {
1268+
return Option.some(a)
1269+
}
1270+
}
1271+
return Option.none()
1272+
}
1273+
)

packages/effect/test/Record.test.ts

+26
Original file line numberDiff line numberDiff line change
@@ -373,5 +373,31 @@ describe("Record", () => {
373373
it("mapEntries", () => {
374374
deepStrictEqual(pipe(stringRecord, Record.mapEntries((a, key) => [key.toUpperCase(), a + 1])), { A: 2 })
375375
})
376+
377+
describe("findFirst", () => {
378+
it("refinement/predicate", () => {
379+
const record = {
380+
a: 1,
381+
b: 2,
382+
c: 1
383+
}
384+
deepStrictEqual(
385+
pipe(record, Record.findFirst((v) => v < 2)),
386+
Option.some(["a", 1])
387+
)
388+
deepStrictEqual(
389+
pipe(record, Record.findFirst((v, k) => v < 2 && k !== "a")),
390+
Option.some(["c", 1])
391+
)
392+
deepStrictEqual(
393+
pipe(record, Record.findFirst((v) => v > 2)),
394+
Option.none()
395+
)
396+
deepStrictEqual(
397+
Record.findFirst(record, (v) => v < 2),
398+
Option.some(["a", 1])
399+
)
400+
})
401+
})
376402
})
377403
})

0 commit comments

Comments
 (0)