1/**
2 * Works with plain JavaScript records as immutable key/value dictionaries.
3 *
4 * A record is an object whose keys are strings or symbols. This module includes
5 * helpers for construction, lookup, updates, mapping, filtering, folding,
6 * set-like combination, and typed conversions between records and iterable
7 * entries. Helpers that change values return new records instead of mutating the
8 * input.
9 *
10 * @since 2.0.0
11 */
12
13import type * as Combiner from "./Combiner.ts"
14import * as Equal from "./Equal.ts"
15import type { Equivalence } from "./Equivalence.ts"
16import { dual, identity } from "./Function.ts"
17import type { TypeLambda } from "./HKT.ts"
18import * as InternalRecord from "./internal/record.ts"
19import * as Option from "./Option.ts"
20import * as Reducer from "./Reducer.ts"
21import type { Result } from "./Result.ts"
22import * as R from "./Result.ts"
23import type { NoInfer } from "./Types.ts"
24
25/**
26 * Represents a readonly record with keys of type `K` and values of type `A`.
27 * This is the foundational type for immutable key-value mappings in Effect.
28 *
29 * **Example** (Defining a readonly record type)
30 *
31 * ```ts import.meta.vitest
32 * import type { Record } from "effect"
33 *
34 * // Creating a readonly record type
35 * type UserRecord = Record.ReadonlyRecord<"name" | "age", string | number>
36 *
37 * const user: UserRecord = {
38 * name: "John",
39 * age: 30
40 * }
41 * user // => { name: "John", age: 30 }
42 * ```
43 *
44 * @category models
45 * @since 2.0.0
46 */
47export type ReadonlyRecord<in out K extends string | symbol, out A> = {
48 readonly [P in K]: A
49}
50
51/**
52 * Namespace containing utility types for working with readonly records.
53 * These types help with type-level operations on record keys and values.
54 *
55 * **Example** (Using readonly record helper types)
56 *
57 * ```ts import.meta.vitest
58 * import type { Record } from "effect"
59 *
60 * // Using NonLiteralKey to convert literal keys to generic types
61 * type GenericKey = Record.ReadonlyRecord.NonLiteralKey<"foo" | "bar"> // string
62 *
63 * // Using IntersectKeys to find common keys between record types
64 * type CommonKeys = Record.ReadonlyRecord.IntersectKeys<"a" | "b", "b" | "c"> // "b"
65 *
66 * "key" satisfies GenericKey
67 * "b" satisfies CommonKeys
68 * ```
69 *
70 * @since 2.0.0
71 */
72export declare namespace ReadonlyRecord {
73 type IsFiniteString<T extends string> = T extends "" ? true :
74 [T] extends [`${infer Head}${infer Rest}`]
75 ? string extends Head ? false : `${number}` extends Head ? false : Rest extends "" ? true : IsFiniteString<Rest>
76 : false
77
78 /**
79 * Represents a type that converts literal string keys to generic string type and symbol keys to generic symbol type.
80 * This is useful for maintaining type safety while allowing flexible key types in record operations.
81 *
82 * **Example** (Converting literal keys to non-literal keys)
83 *
84 * ```ts import.meta.vitest
85 * import type { Record } from "effect"
86 *
87 * // For literal string keys, this becomes 'string'
88 * type Example1 = Record.ReadonlyRecord.NonLiteralKey<"foo" | "bar"> // string
89 *
90 * // For symbol keys, this becomes 'symbol'
91 * type Example2 = Record.ReadonlyRecord.NonLiteralKey<symbol> // symbol
92 *
93 * const symbol: Example2 = Symbol.for("key")
94 * "key" satisfies Example1
95 * symbol
96 * ```
97 *
98 * @category models
99 * @since 2.0.0
100 */
101 export type NonLiteralKey<K extends string | symbol> = K extends string ? IsFiniteString<K> extends true ? string : K
102 : symbol
103
104 /**
105 * Represents the intersection of two key types, handling both literal and non-literal string keys.
106 * This type is used in record operations that need to compute overlapping keys.
107 *
108 * **Example** (Intersecting record keys)
109 *
110 * ```ts import.meta.vitest
111 * import type { Record } from "effect"
112 *
113 * // Intersection of literal keys
114 * type Example1 = Record.ReadonlyRecord.IntersectKeys<"a" | "b", "b" | "c"> // "b"
115 *
116 * // Intersection with generic string
117 * type Example2 = Record.ReadonlyRecord.IntersectKeys<string, "a" | "b"> // string
118 *
119 * "b" satisfies Example1
120 * "a" satisfies Example2
121 * ```
122 *
123 * @category models
124 * @since 2.0.0
125 */
126 export type IntersectKeys<K1 extends string, K2 extends string> = [string] extends [K1 | K2] ?
127 NonLiteralKey<K1> & NonLiteralKey<K2>
128 : K1 & K2
129
130 /**
131 * Represents a record produced by grouping values under keys of type `K`.
132 *
133 * **Details**
134 *
135 * Finite string or symbol key types become optional because an input may not
136 * produce every possible key. Open `string` and `symbol` key types retain their
137 * record index signatures.
138 *
139 * @category utility types
140 * @since 4.0.0
141 */
142 export type GroupByResult<K extends string | symbol, V> = [NonLiteralKey<K>] extends [K] ? Record<K, V>
143 : Partial<Record<K, V>>
144}
145
146/**
147 * Type lambda for readonly records, used in higher-kinded type operations.
148 * This enables records to work with generic type constructors and functors.
149 *
150 * **Example** (Applying a readonly record type lambda)
151 *
152 * ```ts import.meta.vitest
153 * import type { HKT, Record } from "effect"
154 *
155 * type Settings = HKT.Kind<
156 * Record.ReadonlyRecordTypeLambda<"port" | "retries">,
157 * never,
158 * never,
159 * never,
160 * number
161 * >
162 *
163 * const defaults: Settings = {
164 * port: 3000,
165 * retries: 3
166 * }
167 * defaults // => { port: 3000, retries: 3 }
168 * ```
169 *
170 * @category utility types
171 * @since 2.0.0
172 */
173export interface ReadonlyRecordTypeLambda<K extends string = string> extends TypeLambda {
174 readonly type: ReadonlyRecord<K, this["Target"]>
175}
176
177/**
178 * Creates a new, empty record.
179 *
180 * **Example** (Creating an empty record)
181 *
182 * ```ts import.meta.vitest
183 * import { Record } from "effect"
184 *
185 * // Create an empty record
186 * const emptyRecord = Record.empty<string, number>()
187 * emptyRecord // => {}
188 *
189 * // The type ensures type safety for future operations
190 * Record.set(emptyRecord, "count", 42) // => { count: 42 }
191 * ```
192 *
193 * @category constructors
194 * @since 2.0.0
195 */
196export const empty = <K extends string | symbol = never, V = never>(): Record<
197 ReadonlyRecord.NonLiteralKey<K>,
198 V
199> => ({} as any)
200
201/**
202 * Determines if a mutable record is empty.
203 *
204 * **Example** (Checking for an empty record)
205 *
206 * ```ts import.meta.vitest
207 * import { Record } from "effect"
208 *
209 * Record.isEmptyRecord({}) // => true
210 * Record.isEmptyRecord({ a: 3 }) // => false
211 * ```
212 *
213 * @category guards
214 * @since 2.0.0
215 */
216export const isEmptyRecord = <K extends string, A>(self: Record<K, A>): self is Record<K, never> =>
217 Object.keys(self).length === 0
218
219/**
220 * Determines if a readonly record is empty.
221 *
222 * **Example** (Checking for an empty readonly record)
223 *
224 * ```ts import.meta.vitest
225 * import { Record } from "effect"
226 *
227 * Record.isEmptyReadonlyRecord({}) // => true
228 * Record.isEmptyReadonlyRecord({ a: 3 }) // => false
229 * ```
230 *
231 * @category guards
232 * @since 2.0.0
233 */
234export const isEmptyReadonlyRecord: <K extends string, A>(
235 self: ReadonlyRecord<K, A>
236) => self is ReadonlyRecord<K, never> = isEmptyRecord
237
238/**
239 * Takes an iterable and a projection function and returns a record.
240 * The projection function maps each value of the iterable to a tuple of a key and a value, which is then added to the resulting record.
241 *
242 * **Example** (Building a record from mapped iterable values)
243 *
244 * ```ts import.meta.vitest
245 * import { Record } from "effect"
246 *
247 * Record.fromIterableWith([1, 2, 3, 4], (a) => [String(a), a * 2]) // => { "1": 2, "2": 4, "3": 6, "4": 8 }
248 * ```
249 *
250 * @category constructors
251 * @since 2.0.0
252 */
253export const fromIterableWith: {
254 <A, K extends string | symbol, B>(
255 f: (a: A) => readonly [K, B]
256 ): (self: Iterable<A>) => Record<ReadonlyRecord.NonLiteralKey<K>, B>
257 <A, K extends string | symbol, B>(
258 self: Iterable<A>,
259 f: (a: A) => readonly [K, B]
260 ): Record<ReadonlyRecord.NonLiteralKey<K>, B>
261} = dual(
262 2,
263 <A, K extends string, B>(
264 self: Iterable<A>,
265 f: (a: A) => readonly [K, B]
266 ): Record<ReadonlyRecord.NonLiteralKey<K>, B> => {
267 const out: Record<string, B> = empty()
268 for (const a of self) {
269 const [k, b] = f(a)
270 InternalRecord.assignProperty(out, k, b)
271 }
272 return out
273 }
274)
275
276/**
277 * Creates a new record from an iterable, utilizing the provided function to determine the key for each element.
278 *
279 * **Example** (Building a record keyed by iterable values)
280 *
281 * ```ts import.meta.vitest
282 * import { Record } from "effect"
283 *
284 * const users = [
285 * { id: "2", name: "name2" },
286 * { id: "1", name: "name1" }
287 * ]
288 *
289 * Record.fromIterableBy(
290 * users,
291 * (user) => user.id
292 * ) // => { "1": { id: "1", name: "name1" }, "2": { id: "2", name: "name2" } }
293 * ```
294 *
295 * @category constructors
296 * @since 2.0.0
297 */
298export const fromIterableBy: {
299 <A, K extends string | symbol>(
300 f: (a: A) => K
301 ): (items: Iterable<A>) => Record<ReadonlyRecord.NonLiteralKey<K>, A>
302 <A, K extends string | symbol>(
303 items: Iterable<A>,
304 f: (a: A) => K
305 ): Record<ReadonlyRecord.NonLiteralKey<K>, A>
306} = dual(
307 2,
308 <A, K extends string | symbol>(
309 items: Iterable<A>,
310 f: (a: A) => K
311 ): Record<ReadonlyRecord.NonLiteralKey<K>, A> => fromIterableWith(items, (a) => [f(a), a])
312)
313
314/**
315 * Builds a record from an iterable of key-value pairs.
316 *
317 * **Details**
318 *
319 * If there are conflicting keys when using `fromEntries`, the last occurrence of the key/value pair will overwrite the
320 * previous ones. So the resulting record will only have the value of the last occurrence of each key.
321 *
322 * **Example** (Building a record from entries)
323 *
324 * ```ts import.meta.vitest
325 * import { Record } from "effect"
326 *
327 * Record.fromEntries([["a", 1], ["b", 2]]) // => { a: 1, b: 2 }
328 * ```
329 *
330 * @category constructors
331 * @since 2.0.0
332 */
333export const fromEntries: <Entry extends readonly [string | symbol, any]>(
334 entries: Iterable<Entry>
335) => Record<ReadonlyRecord.NonLiteralKey<Entry[0]>, Entry[1]> = Object.fromEntries
336
337/**
338 * Transforms the values of a record into an `Array` with a custom mapping function.
339 *
340 * **Example** (Collecting mapped record values)
341 *
342 * ```ts import.meta.vitest
343 * import { Record } from "effect"
344 *
345 * const x = { a: 1, b: 2, c: 3 }
346 * Record.collect(x, (key, n) => [key, n]) // => [["a", 1], ["b", 2], ["c", 3]]
347 * ```
348 *
349 * @category converting
350 * @since 2.0.0
351 */
352export const collect: {
353 <K extends string, A, B>(f: (key: K, a: A) => B): (self: ReadonlyRecord<K, A>) => Array<B>
354 <K extends string, A, B>(self: ReadonlyRecord<K, A>, f: (key: K, a: A) => B): Array<B>
355} = dual(
356 2,
357 <K extends string, A, B>(self: ReadonlyRecord<K, A>, f: (key: K, a: A) => B): Array<B> => {
358 const out: Array<B> = []
359 for (const key of keys(self)) {
360 out.push(f(key, self[key]))
361 }
362 return out
363 }
364)
365
366/**
367 * Takes a record and returns an array of tuples containing its keys and values.
368 *
369 * **Example** (Converting a record to entries)
370 *
371 * ```ts import.meta.vitest
372 * import { Record } from "effect"
373 *
374 * const x = { a: 1, b: 2, c: 3 }
375 * Record.toEntries(x) // => [["a", 1], ["b", 2], ["c", 3]]
376 * ```
377 *
378 * @category converting
379 * @since 2.0.0
380 */
381export const toEntries: <K extends string, A>(self: ReadonlyRecord<K, A>) => Array<[K, A]> = collect((
382 key,
383 value
384) => [key, value])
385
386/**
387 * Returns the number of key/value pairs in a record.
388 *
389 * **Example** (Getting the record size)
390 *
391 * ```ts import.meta.vitest
392 * import { Record } from "effect"
393 *
394 * Record.size({ a: "a", b: 1, c: true }) // => 3
395 * ```
396 *
397 * @category getters
398 * @since 2.0.0
399 */
400export const size = <K extends string, A>(self: ReadonlyRecord<K, A>): number => keys(self).length
401
402/**
403 * Checks whether a given `key` exists in a record.
404 *
405 * **Example** (Checking key membership)
406 *
407 * ```ts import.meta.vitest
408 * import { Record } from "effect"
409 *
410 * Record.has({ a: 1, b: 2 }, "a") // => true
411 * Record.has(Record.empty<string>(), "c") // => false
412 * ```
413 *
414 * @category predicates
415 * @since 2.0.0
416 */
417export const has: {
418 <K extends string | symbol>(
419 key: NoInfer<K>
420 ): <A>(self: ReadonlyRecord<K, A>) => boolean
421 <K extends string | symbol, A>(
422 self: ReadonlyRecord<K, A>,
423 key: NoInfer<K>
424 ): boolean
425} = dual(
426 2,
427 <K extends string | symbol, A>(
428 self: ReadonlyRecord<K, A>,
429 key: NoInfer<K>
430 ): boolean => Object.hasOwn(self, key)
431)
432
433/**
434 * Retrieves a value at a particular key from a record safely, returning it wrapped in an `Option`.
435 *
436 * **Example** (Getting a value as an Option)
437 *
438 * ```ts import.meta.vitest
439 * import { Option, Record as R } from "effect"
440 *
441 * const person: Record<string, unknown> = { name: "John Doe", age: 35 }
442 *
443 * R.get(person, "name") // => Option.some("John Doe")
444 * R.get(person, "email") // => Option.none()
445 * ```
446 *
447 * @category getters
448 * @since 2.0.0
449 */
450export const get: {
451 <K extends string | symbol>(key: NoInfer<K>): <A>(self: ReadonlyRecord<K, A>) => Option.Option<A>
452 <K extends string | symbol, A>(self: ReadonlyRecord<K, A>, key: NoInfer<K>): Option.Option<A>
453} = dual(
454 2,
455 <K extends string | symbol, A>(self: ReadonlyRecord<K, A>, key: NoInfer<K>): Option.Option<A> =>
456 Object.hasOwn(self, key) ? Option.some(self[key]) : Option.none()
457)
458
459/**
460 * Applies a function to the element at the specified key safely, creating a new record,
461 * or return `Option.none()` if the key doesn't exist.
462 *
463 * **Example** (Modifying a value at a key)
464 *
465 * ```ts import.meta.vitest
466 * import { Option, Record } from "effect"
467 *
468 * const f = (x: number) => x * 2
469 *
470 * const input: Record<string, number> = { a: 3 }
471 *
472 * Record.modify(input, "a", f) // => Option.some({ a: 6 })
473 * Record.modify(input, "b", f) // => Option.none()
474 * ```
475 *
476 * @category mutations
477 * @since 2.0.0
478 */
479export const modify: {
480 <K extends string | symbol, A, B>(
481 key: NoInfer<K>,
482 f: (a: A) => B
483 ): (self: ReadonlyRecord<K, A>) => Option.Option<Record<K, A | B>>
484 <K extends string | symbol, A, B>(
485 self: ReadonlyRecord<K, A>,
486 key: NoInfer<K>,
487 f: (a: A) => B
488 ): Option.Option<Record<K, A | B>>
489} = dual(
490 3,
491 <K extends string | symbol, A, B>(
492 self: ReadonlyRecord<K, A>,
493 key: NoInfer<K>,
494 f: (a: A) => B
495 ): Option.Option<Record<K, A | B>> => {
496 if (!has(self, key)) return Option.none()
497 return Option.some({ ...self, [key]: f(self[key]) })
498 }
499)
500
501/**
502 * Replaces the value at an existing key safely and returns the updated record in
503 * `Option.some`.
504 *
505 * **Details**
506 *
507 * If the key is not present, returns `Option.none()` and leaves the record
508 * unchanged.
509 *
510 * **Example** (Replacing a value at a key)
511 *
512 * ```ts import.meta.vitest
513 * import { Option, Record } from "effect"
514 *
515 * Record.replace({ a: 1, b: 2, c: 3 }, "a", 10) // => Option.some({ a: 10, b: 2, c: 3 })
516 * Record.replace(Record.empty<string>(), "a", 10) // => Option.none()
517 * ```
518 *
519 * @category mutations
520 * @since 2.0.0
521 */
522export const replace: {
523 <K extends string | symbol, B>(
524 key: NoInfer<K>,
525 b: B
526 ): <A>(self: ReadonlyRecord<K, A>) => Option.Option<Record<K, A | B>>
527 <K extends string | symbol, A, B>(
528 self: ReadonlyRecord<K, A>,
529 key: NoInfer<K>,
530 b: B
531 ): Option.Option<Record<K, A | B>>
532} = dual(
533 3,
534 <K extends string | symbol, A, B>(
535 self: ReadonlyRecord<K, A>,
536 key: NoInfer<K>,
537 b: B
538 ): Option.Option<Record<K, A | B>> => modify(self, key, () => b)
539)
540
541/**
542 * Removes a key from a record.
543 *
544 * **When to use**
545 *
546 * Use to create a shallow copy of a record without one property.
547 *
548 * **Details**
549 *
550 * If the key is not present, the result is still a shallow copy of the original
551 * record.
552 *
553 * **Example** (Removing a key)
554 *
555 * ```ts import.meta.vitest
556 * import { Record } from "effect"
557 *
558 * Record.remove({ a: 1, b: 2 }, "a") // => { b: 2 }
559 * ```
560 *
561 * @category mutations
562 * @since 2.0.0
563 */
564export const remove: {
565 <K extends string | symbol, X extends K>(key: X): <A>(self: ReadonlyRecord<K, A>) => Record<Exclude<K, X>, A>
566 <K extends string | symbol, A, X extends K>(self: ReadonlyRecord<K, A>, key: X): Record<Exclude<K, X>, A>
567} = dual(
568 2,
569 <K extends string | symbol, A, X extends K>(self: ReadonlyRecord<K, A>, key: X): Record<Exclude<K, X>, A> => {
570 if (!has(self, key)) {
571 return { ...self }
572 }
573 const out = { ...self }
574 delete out[key]
575 return out
576 }
577)
578
579/**
580 * Retrieves the value of the property with the given `key` from a record safely and returns an `Option`
581 * of a tuple with the value and the record with the removed property.
582 * If the key is not present, returns `Option.none()`.
583 *
584 * **Example** (Popping a value and removing its key)
585 *
586 * ```ts import.meta.vitest
587 * import { Option, Record } from "effect"
588 *
589 * const input: Record<string, number> = { a: 1, b: 2 }
590 *
591 * Record.pop(input, "a") // => Option.some([1, { b: 2 }])
592 * Record.pop(input, "c") // => Option.none()
593 * ```
594 *
595 * @category mutations
596 * @since 2.0.0
597 */
598export const pop: {
599 <K extends string | symbol, X extends K>(
600 key: X
601 ): <A>(self: ReadonlyRecord<K, A>) => Option.Option<[A, Record<Exclude<K, X>, A>]>
602 <K extends string | symbol, A, X extends K>(
603 self: ReadonlyRecord<K, A>,
604 key: X
605 ): Option.Option<[A, Record<Exclude<K, X>, A>]>
606} = dual(2, <K extends string | symbol, A, X extends K>(
607 self: ReadonlyRecord<K, A>,
608 key: X
609): Option.Option<[A, Record<Exclude<K, X>, A>]> =>
610 has(self, key) ? Option.some([self[key], remove(self, key)]) : Option.none())
611
612/**
613 * Maps a record into another record by applying a transformation function to each of its values.
614 *
615 * **Example** (Mapping record values)
616 *
617 * ```ts import.meta.vitest
618 * import { Record } from "effect"
619 *
620 * const f = (n: number) => `-${n}`
621 *
622 * Record.map({ a: 3, b: 5 }, f) // => { a: "-3", b: "-5" }
623 *
624 * const g = (n: number, key: string) => `${key.toUpperCase()}-${n}`
625 *
626 * Record.map({ a: 3, b: 5 }, g) // => { a: "A-3", b: "B-5" }
627 * ```
628 *
629 * @category mapping
630 * @since 2.0.0
631 */
632export const map: {
633 <K extends string, A, B>(f: (a: A, key: NoInfer<K>) => B): (self: ReadonlyRecord<K, A>) => Record<K, B>
634 <K extends string, A, B>(self: ReadonlyRecord<K, A>, f: (a: A, key: NoInfer<K>) => B): Record<K, B>
635} = dual(
636 2,
637 <K extends string, A, B>(self: ReadonlyRecord<K, A>, f: (a: A, key: NoInfer<K>) => B): Record<K, B> => {
638 const out: Record<K, B> = { ...self } as any
639 for (const key of keys(self)) {
640 InternalRecord.assignProperty(out, key, f(self[key], key))
641 }
642 return out
643 }
644)
645
646/**
647 * Maps the keys of a `ReadonlyRecord` while preserving the corresponding values.
648 *
649 * **Example** (Mapping record keys)
650 *
651 * ```ts import.meta.vitest
652 * import { Record } from "effect"
653 *
654 * Record.mapKeys({ a: 3, b: 5 }, (key) => key.toUpperCase()) // => { A: 3, B: 5 }
655 * ```
656 *
657 * @category mapping
658 * @since 2.0.0
659 */
660export const mapKeys: {
661 <K extends string, A, K2 extends string>(
662 f: (key: K, a: A) => K2
663 ): (self: ReadonlyRecord<K, A>) => Record<K2, A>
664 <K extends string, A, K2 extends string>(
665 self: ReadonlyRecord<K, A>,
666 f: (key: K, a: A) => K2
667 ): Record<K2, A>
668} = dual(
669 2,
670 <K extends string, A, K2 extends string>(
671 self: ReadonlyRecord<K, A>,
672 f: (key: K, a: A) => K2
673 ): Record<K2, A> => {
674 const out: Record<K2, A> = {} as any
675 for (const key of keys(self)) {
676 const a = self[key]
677 InternalRecord.assignProperty(out, f(key, a), a)
678 }
679 return out
680 }
681)
682
683/**
684 * Maps entries of a `ReadonlyRecord` using the provided function, allowing modification of both keys and corresponding values.
685 *
686 * **Example** (Mapping record entries)
687 *
688 * ```ts import.meta.vitest
689 * import { Record } from "effect"
690 *
691 * Record.mapEntries({ a: 3, b: 5 }, (a, key) => [key.toUpperCase(), a + 1]) // => { A: 4, B: 6 }
692 * ```
693 *
694 * @category mapping
695 * @since 2.0.0
696 */
697export const mapEntries: {
698 <K extends string, A, K2 extends string, B>(
699 f: (a: A, key: K) => readonly [K2, B]
700 ): (self: ReadonlyRecord<K, A>) => Record<K2, B>
701 <K extends string, A, K2 extends string, B>(
702 self: ReadonlyRecord<K, A>,
703 f: (a: A, key: K) => [K2, B]
704 ): Record<K2, B>
705} = dual(
706 2,
707 <K extends string, A, K2 extends string, B>(
708 self: ReadonlyRecord<K, A>,
709 f: (a: A, key: K) => [K2, B]
710 ): Record<K2, B> => {
711 const out = {} as Record<K2, B>
712 for (const key of keys(self)) {
713 const [k, b] = f(self[key], key)
714 InternalRecord.assignProperty(out, k, b)
715 }
716 return out
717 }
718)
719
720/**
721 * Transforms a record by applying the function `f` to each key and value in the original record.
722 * If the function succeeds, the key-value pair is included in the output record.
723 *
724 * **Example** (Filtering and mapping with Result)
725 *
726 * ```ts import.meta.vitest
727 * import { Record, Result } from "effect"
728 *
729 * const x = { a: 1, b: 2, c: 3 }
730 * const f = (a: number, key: string) => a > 2 ? Result.succeed(a * 2) : Result.failVoid
731 * Record.filterMap(x, f) // => { c: 6 }
732 * ```
733 *
734 * @category filtering
735 * @since 2.0.0
736 */
737export const filterMap: {
738 <K extends string, A, B, X>(
739 f: (input: A, key: K) => Result<B, X>
740 ): (self: ReadonlyRecord<K, A>) => Record<ReadonlyRecord.NonLiteralKey<K>, B>
741 <K extends string, A, B, X>(
742 self: ReadonlyRecord<K, A>,
743 f: (input: A, key: K) => Result<B, X>
744 ): Record<ReadonlyRecord.NonLiteralKey<K>, B>
745} = dual(
746 2,
747 <K extends string, A, B, X>(
748 self: ReadonlyRecord<K, A>,
749 f: (input: A, key: K) => Result<B, X>
750 ): Record<ReadonlyRecord.NonLiteralKey<K>, B> => {
751 const out: Record<string, B> = empty()
752 for (const key of keys(self)) {
753 const result = f(self[key], key)
754 if (R.isSuccess(result)) {
755 InternalRecord.assignProperty(out, key, result.success)
756 }
757 }
758 return out
759 }
760)
761
762/**
763 * Selects properties from a record whose values match the given predicate.
764 *
765 * **Example** (Filtering record values)
766 *
767 * ```ts import.meta.vitest
768 * import { Record } from "effect"
769 *
770 * const x = { a: 1, b: 2, c: 3, d: 4 }
771 * Record.filter(x, (n) => n > 2) // => { c: 3, d: 4 }
772 * ```
773 *
774 * @category filtering
775 * @since 2.0.0
776 */
777export const filter: {
778 <K extends string, A, B extends A>(
779 refinement: (a: NoInfer<A>, key: K) => a is B
780 ): (self: ReadonlyRecord<K, A>) => Record<ReadonlyRecord.NonLiteralKey<K>, B>
781 <K extends string, A>(
782 predicate: (A: NoInfer<A>, key: K) => boolean
783 ): (self: ReadonlyRecord<K, A>) => Record<ReadonlyRecord.NonLiteralKey<K>, A>
784 <K extends string, A, B extends A>(
785 self: ReadonlyRecord<K, A>,
786 refinement: (a: A, key: K) => a is B
787 ): Record<ReadonlyRecord.NonLiteralKey<K>, B>
788 <K extends string, A>(
789 self: ReadonlyRecord<K, A>,
790 predicate: (a: A, key: K) => boolean
791 ): Record<ReadonlyRecord.NonLiteralKey<K>, A>
792} = dual(
793 2,
794 <K extends string, A>(
795 self: ReadonlyRecord<K, A>,
796 predicate: (a: A, key: K) => boolean
797 ): Record<ReadonlyRecord.NonLiteralKey<K>, A> => {
798 const out: Record<string, A> = empty()
799 for (const key of keys(self)) {
800 if (predicate(self[key], key)) {
801 InternalRecord.assignProperty(out, key, self[key])
802 }
803 }
804 return out
805 }
806)
807
808/**
809 * Returns a new record containing only the `Some` values from a record of
810 * `Option` values, preserving the original keys.
811 *
812 * **Example** (Extracting Some values)
813 *
814 * ```ts import.meta.vitest
815 * import { Option, Record } from "effect"
816 *
817 * Record.getSomes({ a: Option.some(1), b: Option.none(), c: Option.some(2) }) // => { a: 1, c: 2 }
818 * ```
819 *
820 * @category filtering
821 * @since 2.0.0
822 */
823export const getSomes: <K extends string, A>(
824 self: ReadonlyRecord<K, Option.Option<A>>
825) => Record<ReadonlyRecord.NonLiteralKey<K>, A> = <K extends string, A>(
826 self: ReadonlyRecord<K, Option.Option<A>>
827): Record<ReadonlyRecord.NonLiteralKey<K>, A> => {
828 const out: Record<string, A> = empty()
829 for (const key of keys(self)) {
830 const option = self[key]
831 if (Option.isSome(option)) {
832 InternalRecord.assignProperty(out, key, option.value)
833 }
834 }
835 return out
836}
837
838/**
839 * Returns a new record containing only the `Err` values from a record of
840 * `Result` values, preserving the original keys.
841 *
842 * **Example** (Extracting Result failures)
843 *
844 * ```ts import.meta.vitest
845 * import { Record, Result } from "effect"
846 *
847 * Record.getFailures({
848 * a: Result.succeed(1),
849 * b: Result.fail("err"),
850 * c: Result.succeed(2)
851 * }) // => { b: "err" }
852 * ```
853 *
854 * @category filtering
855 * @since 4.0.0
856 */
857export const getFailures = <K extends string, A, E>(
858 self: ReadonlyRecord<K, Result<A, E>>
859): Record<ReadonlyRecord.NonLiteralKey<K>, E> => {
860 const out: Record<string, E> = empty()
861 for (const key of keys(self)) {
862 const value = self[key]
863 if (R.isFailure(value)) {
864 InternalRecord.assignProperty(out, key, value.failure)
865 }
866 }
867
868 return out
869}
870
871/**
872 * Returns a new record containing only the `Ok` values from a record of
873 * `Result` values, preserving the original keys.
874 *
875 * **Example** (Extracting Result successes)
876 *
877 * ```ts import.meta.vitest
878 * import { Record, Result } from "effect"
879 *
880 * Record.getSuccesses({
881 * a: Result.succeed(1),
882 * b: Result.fail("err"),
883 * c: Result.succeed(2)
884 * }) // => { a: 1, c: 2 }
885 * ```
886 *
887 * @category filtering
888 * @since 4.0.0
889 */
890export const getSuccesses = <K extends string, A, E>(
891 self: ReadonlyRecord<K, Result<A, E>>
892): Record<string, A> => {
893 const out: Record<string, A> = empty()
894 for (const key of keys(self)) {
895 const value = self[key]
896 if (R.isSuccess(value)) {
897 InternalRecord.assignProperty(out, key, value.success)
898 }
899 }
900
901 return out
902}
903
904/**
905 * Applies a function to each record entry and partitions the returned `Result`
906 * values into two records.
907 *
908 * **Details**
909 *
910 * Success values are collected in the first record, and failure values are
911 * collected in the second record, preserving the original keys.
912 *
913 * **Example** (Partitioning with Result)
914 *
915 * ```ts import.meta.vitest
916 * import { Record, Result } from "effect"
917 *
918 * const x = { a: 1, b: 2, c: 3 }
919 * const f = (n: number) => (n % 2 === 0 ? Result.succeed(n) : Result.fail(n))
920 * Record.partition(x, f) // => [{ b: 2 }, { a: 1, c: 3 }]
921 * ```
922 *
923 * @category filtering
924 * @since 2.0.0
925 */
926export const partition: {
927 <K extends string, A, B, C>(
928 f: (input: A, key: K) => Result<C, B>
929 ): (
930 self: ReadonlyRecord<K, A>
931 ) => [passes: Record<ReadonlyRecord.NonLiteralKey<K>, C>, fails: Record<ReadonlyRecord.NonLiteralKey<K>, B>]
932 <K extends string, A, B, C>(
933 self: ReadonlyRecord<K, A>,
934 f: (input: A, key: K) => Result<C, B>
935 ): [passes: Record<ReadonlyRecord.NonLiteralKey<K>, C>, fails: Record<ReadonlyRecord.NonLiteralKey<K>, B>]
936} = dual(
937 2,
938 <K extends string, A, B, C>(
939 self: ReadonlyRecord<K, A>,
940 f: (input: A, key: K) => Result<C, B>
941 ): [passes: Record<ReadonlyRecord.NonLiteralKey<K>, C>, fails: Record<ReadonlyRecord.NonLiteralKey<K>, B>] => {
942 const passes: Record<string, C> = empty()
943 const fails: Record<string, B> = empty()
944 for (const key of keys(self)) {
945 const e = f(self[key], key)
946 if (R.isFailure(e)) {
947 InternalRecord.assignProperty(fails, key, e.failure)
948 } else {
949 InternalRecord.assignProperty(passes, key, e.success)
950 }
951 }
952 return [passes, fails]
953 }
954)
955
956/**
957 * Partitions a record of `Result` values into two separate records,
958 * one with the success values and one with the failure values.
959 *
960 * **Example** (Separating Result values)
961 *
962 * ```ts import.meta.vitest
963 * import { Record, Result } from "effect"
964 *
965 * Record.separate({ a: Result.fail("e"), b: Result.succeed(1) }) // => [{ b: 1 }, { a: "e" }]
966 * ```
967 *
968 * @category filtering
969 * @since 2.0.0
970 */
971export const separate: <K extends string, A, B>(
972 self: ReadonlyRecord<K, Result<B, A>>
973) => [Record<ReadonlyRecord.NonLiteralKey<K>, B>, Record<ReadonlyRecord.NonLiteralKey<K>, A>] = partition(identity)
974
975/**
976 * Retrieves the keys of a given record as an array.
977 *
978 * **Example** (Getting record keys)
979 *
980 * ```ts import.meta.vitest
981 * import { Record } from "effect"
982 *
983 * Record.keys({ a: 1, b: 2, c: 3 }) // => ["a", "b", "c"]
984 * ```
985 *
986 * @category getters
987 * @since 2.0.0
988 */
989export const keys = <K extends string | symbol, A>(self: ReadonlyRecord<K, A>): Array<K & string> =>
990 Object.keys(self) as Array<K & string>
991
992/**
993 * Retrieves the values of a given record as an array.
994 *
995 * **Example** (Getting record values)
996 *
997 * ```ts import.meta.vitest
998 * import { Record } from "effect"
999 *
1000 * Record.values({ a: 1, b: 2, c: 3 }) // => [1, 2, 3]
1001 * ```
1002 *
1003 * @category getters
1004 * @since 2.0.0
1005 */
1006export const values = <K extends string, A>(self: ReadonlyRecord<K, A>): Array<A> => collect(self, (_, a) => a)
1007
1008/**
1009 * Adds a new key-value pair or update an existing key's value in a record.
1010 *
1011 * **Example** (Setting a record value)
1012 *
1013 * ```ts import.meta.vitest
1014 * import { Record } from "effect"
1015 *
1016 * Record.set("a", 5)({ a: 1, b: 2 }) // => { a: 5, b: 2 }
1017 * Record.set("c", 5)({ a: 1, b: 2 }) // => { a: 1, b: 2, c: 5 }
1018 * ```
1019 *
1020 * @category mutations
1021 * @since 2.0.0
1022 */
1023export const set: {
1024 <K extends string | symbol, K1 extends K | ((string | symbol) & {}), B>(
1025 key: K1,
1026 value: B
1027 ): <A>(self: ReadonlyRecord<K, A>) => Record<K | K1, A | B>
1028 <K extends string | symbol, A, K1 extends K | ((string | symbol) & {}), B>(
1029 self: ReadonlyRecord<K, A>,
1030 key: K1,
1031 value: B
1032 ): Record<K | K1, A | B>
1033} = dual(
1034 3,
1035 <K extends string | symbol, A, K1 extends K | ((string | symbol) & {}), B>(
1036 self: ReadonlyRecord<K, A>,
1037 key: K1,
1038 value: B
1039 ): Record<K | K1, A | B> => {
1040 return { ...self, [key]: value } as any
1041 }
1042)
1043
1044/**
1045 * Mutates a record by assigning a value to a property.
1046 *
1047 * **When to use**
1048 *
1049 * Use when incrementally constructing a new record and copying it for every
1050 * property would be unnecessary.
1051 *
1052 * **Gotchas**
1053 *
1054 * This function mutates `self`. When `key` is `"__proto__"`, it creates an
1055 * own data property instead of changing the object's prototype.
1056 *
1057 * **Example** (Assigning an external key safely)
1058 *
1059 * ```ts import.meta.vitest
1060 * import { Record } from "effect"
1061 *
1062 * const key: string = "__proto__" // Assume this comes from external input
1063 * const value = { polluted: true }
1064 *
1065 * const unsafe: Record<string, unknown> = {}
1066 * unsafe[key] = value
1067 * Object.getPrototypeOf(unsafe) === value // => true
1068 *
1069 * const safe: Record<string, unknown> = {}
1070 * Record.assignProperty(safe, key, value)
1071 * Object.getPrototypeOf(safe) === Object.prototype // => true
1072 * safe[key] === value // => true
1073 * ```
1074 *
1075 * @see {@link set} for an immutable update
1076 * @category mutations
1077 * @since 4.0.0
1078 */
1079export const assignProperty: (self: object, key: PropertyKey, value: unknown) => void = InternalRecord.assignProperty
1080
1081/**
1082 * Checks whether all the keys and values in one record are also found in another record.
1083 * Uses the provided equivalence function to compare values.
1084 *
1085 * **Example** (Checking subrecords with a custom equivalence)
1086 *
1087 * ```ts import.meta.vitest
1088 * import { Equivalence, Record } from "effect"
1089 *
1090 * const isSubrecord = Record.isSubrecordBy(
1091 * Equivalence.make<string>((self, that) => self.toLowerCase() === that.toLowerCase())
1092 * )
1093 *
1094 * const required: Record.ReadonlyRecord<string, string> = { role: "Admin" }
1095 * const available: Record.ReadonlyRecord<string, string> = {
1096 * role: "admin",
1097 * status: "active"
1098 * }
1099 *
1100 * isSubrecord(required, available) // => true
1101 * isSubrecord({ role: "Admin", status: "inactive" }, available) // => false
1102 * isSubrecord(required, { role: "editor", status: "active" }) // => false
1103 * ```
1104 *
1105 * @category predicates
1106 * @since 2.0.0
1107 */
1108export const isSubrecordBy = <A>(equivalence: Equivalence<A>): {
1109 <K extends string>(that: ReadonlyRecord<K, A>): (self: ReadonlyRecord<K, A>) => boolean
1110 <K extends string>(self: ReadonlyRecord<K, A>, that: ReadonlyRecord<K, A>): boolean
1111} =>
1112 dual(2, <K extends string>(self: ReadonlyRecord<K, A>, that: ReadonlyRecord<K, A>): boolean => {
1113 for (const key of keys(self)) {
1114 if (!has(that, key) || !equivalence(self[key], that[key])) {
1115 return false
1116 }
1117 }
1118 return true
1119 })
1120
1121/**
1122 * Checks whether the first record is a subrecord of the second record.
1123 *
1124 * **Details**
1125 *
1126 * Returns `true` when every key and value in `self` is also present in `that`.
1127 * Values are compared with Effect equality via `Equal.asEquivalence()`.
1128 *
1129 * **Example** (Checking subrecords)
1130 *
1131 * ```ts import.meta.vitest
1132 * import { Record } from "effect"
1133 *
1134 * Record.isSubrecord({ a: 1 } as Record<string, number>, { a: 1, b: 2 }) // => true
1135 * Record.isSubrecord({ a: 1, b: 2 }, { a: 1 } as Record<string, number>) // => false
1136 * ```
1137 *
1138 * @category predicates
1139 * @since 2.0.0
1140 */
1141export const isSubrecord: {
1142 <K extends string, A>(that: ReadonlyRecord<K, A>): (self: ReadonlyRecord<K, A>) => boolean
1143 <K extends string, A>(self: ReadonlyRecord<K, A>, that: ReadonlyRecord<K, A>): boolean
1144} = isSubrecordBy(Equal.asEquivalence())
1145
1146/**
1147 * Reduces a record to a single value by combining its entries with a specified function.
1148 *
1149 * **Example** (Reducing record values)
1150 *
1151 * ```ts import.meta.vitest
1152 * import { Record } from "effect"
1153 *
1154 * Record.reduce({ a: 1, b: 2, c: 3 }, 0, (acc, value) => acc + value) // => 6
1155 * ```
1156 *
1157 * @category folding
1158 * @since 2.0.0
1159 */
1160export const reduce: {
1161 <Z, V, K extends string>(
1162 zero: Z,
1163 f: (accumulator: Z, value: V, key: K) => Z
1164 ): (self: ReadonlyRecord<K, V>) => Z
1165 <K extends string, V, Z>(self: ReadonlyRecord<K, V>, zero: Z, f: (accumulator: Z, value: V, key: K) => Z): Z
1166} = dual(
1167 3,
1168 <K extends string, V, Z>(
1169 self: ReadonlyRecord<K, V>,
1170 zero: Z,
1171 f: (accumulator: Z, value: V, key: K) => Z
1172 ): Z => {
1173 let out: Z = zero
1174 for (const key of keys(self)) {
1175 out = f(out, self[key], key)
1176 }
1177 return out
1178 }
1179)
1180
1181/**
1182 * Checks whether all entries in a record meet a specific condition.
1183 *
1184 * **Example** (Checking every record value)
1185 *
1186 * ```ts import.meta.vitest
1187 * import { Record } from "effect"
1188 *
1189 * Record.every({ a: 1, b: 2 }, (n) => n > 0) // => true
1190 * Record.every({ a: 1, b: -1 }, (n) => n > 0) // => false
1191 * ```
1192 *
1193 * @category guards
1194 * @since 2.0.0
1195 */
1196export const every: {
1197 <A, K extends string, B extends A>(
1198 refinement: (value: A, key: K) => value is B
1199 ): (self: ReadonlyRecord<K, A>) => self is ReadonlyRecord<K, B>
1200 <A, K extends string>(predicate: (value: A, key: K) => boolean): (self: ReadonlyRecord<K, A>) => boolean
1201 <A, K extends string, B extends A>(
1202 self: ReadonlyRecord<K, A>,
1203 refinement: (value: A, key: K) => value is B
1204 ): self is ReadonlyRecord<K, B>
1205 <K extends string, A>(self: ReadonlyRecord<K, A>, predicate: (value: A, key: K) => boolean): boolean
1206} = dual(
1207 2,
1208 <A, K extends string, B extends A>(
1209 self: ReadonlyRecord<K, A>,
1210 refinement: (value: A, key: K) => value is B
1211 ): self is ReadonlyRecord<K, B> => {
1212 for (const key of keys(self)) {
1213 if (!refinement(self[key], key)) {
1214 return false
1215 }
1216 }
1217 return true
1218 }
1219)
1220
1221/**
1222 * Checks whether any entry in a record meets a specific condition.
1223 *
1224 * **Example** (Checking for any matching value)
1225 *
1226 * ```ts import.meta.vitest
1227 * import { Record } from "effect"
1228 *
1229 * Record.some({ a: 1, b: 2 }, (n) => n > 1) // => true
1230 * Record.some({ a: 1, b: 2 }, (n) => n > 2) // => false
1231 * ```
1232 *
1233 * @category predicates
1234 * @since 2.0.0
1235 */
1236export const some: {
1237 <A, K extends string>(predicate: (value: A, key: K) => boolean): (self: ReadonlyRecord<K, A>) => boolean
1238 <K extends string, A>(self: ReadonlyRecord<K, A>, predicate: (value: A, key: K) => boolean): boolean
1239} = dual(
1240 2,
1241 <K extends string, A>(self: ReadonlyRecord<K, A>, predicate: (value: A, key: K) => boolean): boolean => {
1242 for (const key of keys(self)) {
1243 if (predicate(self[key], key)) {
1244 return true
1245 }
1246 }
1247 return false
1248 }
1249)
1250
1251/**
1252 * Merges two records, preserving entries that exist in either of the records.
1253 * For keys that exist in both records, the provided combine function is used to merge the values.
1254 *
1255 * **Example** (Merging records with union)
1256 *
1257 * ```ts import.meta.vitest
1258 * import { Record } from "effect"
1259 *
1260 * Record.union({ a: 1, b: 2 }, { b: 3, c: 4 }, (a, b) => a + b) // => { a: 1, b: 5, c: 4 }
1261 * ```
1262 *
1263 * @category combining
1264 * @since 2.0.0
1265 */
1266export const union: {
1267 <K1 extends string, A, B, C>(
1268 that: ReadonlyRecord<K1, B>,
1269 combine: (selfValue: A, thatValue: B) => C
1270 ): <K0 extends string>(self: ReadonlyRecord<K0, A>) => Record<K0 | K1, A | B | C>
1271 <K0 extends string, A, K1 extends string, B, C>(
1272 self: ReadonlyRecord<K0, A>,
1273 that: ReadonlyRecord<K1, B>,
1274 combine: (selfValue: A, thatValue: B) => C
1275 ): Record<K0 | K1, A | B | C>
1276} = dual(
1277 3,
1278 <K0 extends string, A, K1 extends string, B, C>(
1279 self: ReadonlyRecord<K0, A>,
1280 that: ReadonlyRecord<K1, B>,
1281 combine: (selfValue: A, thatValue: B) => C
1282 ): Record<K0 | K1, A | B | C> => {
1283 if (isEmptyRecord(self)) {
1284 return { ...that } as any
1285 }
1286 if (isEmptyRecord(that)) {
1287 return { ...self } as any
1288 }
1289 const out: Record<string, A | B | C> = empty()
1290 for (const key of keys(self)) {
1291 if (has(that, key as any)) {
1292 InternalRecord.assignProperty(out, key, combine(self[key], that[key as unknown as K1]))
1293 } else {
1294 InternalRecord.assignProperty(out, key, self[key])
1295 }
1296 }
1297 for (const key of keys(that)) {
1298 if (!has(out, key)) {
1299 InternalRecord.assignProperty(out, key, that[key])
1300 }
1301 }
1302 return out
1303 }
1304)
1305
1306/**
1307 * Merges two records, retaining only the entries that exist in both records.
1308 * For intersecting keys, the provided combine function is used to merge the values.
1309 *
1310 * **Example** (Merging intersecting keys)
1311 *
1312 * ```ts import.meta.vitest
1313 * import { Record } from "effect"
1314 *
1315 * Record.intersection({ a: 1, b: 2 }, { b: 3, c: 4 }, (a, b) => a + b) // => { b: 5 }
1316 * ```
1317 *
1318 * @category combining
1319 * @since 2.0.0
1320 */
1321export const intersection: {
1322 <K1 extends string, A, B, C>(
1323 that: ReadonlyRecord<K1, B>,
1324 combine: (selfValue: A, thatValue: B) => C
1325 ): <K0 extends string>(self: ReadonlyRecord<K0, A>) => Record<ReadonlyRecord.IntersectKeys<K0, K1>, C>
1326 <K0 extends string, A, K1 extends string, B, C>(
1327 self: ReadonlyRecord<K0, A>,
1328 that: ReadonlyRecord<K1, B>,
1329 combine: (selfValue: A, thatValue: B) => C
1330 ): Record<ReadonlyRecord.IntersectKeys<K0, K1>, C>
1331} = dual(
1332 3,
1333 <K0 extends string, A, K1 extends string, B, C>(
1334 self: ReadonlyRecord<K0, A>,
1335 that: ReadonlyRecord<K1, B>,
1336 combine: (selfValue: A, thatValue: B) => C
1337 ): Record<ReadonlyRecord.IntersectKeys<K0, K1>, C> => {
1338 const out: Record<string, C> = empty()
1339 if (isEmptyRecord(self) || isEmptyRecord(that)) {
1340 return out
1341 }
1342 for (const key of keys(self)) {
1343 if (has(that, key as any)) {
1344 InternalRecord.assignProperty(out, key, combine(self[key], that[key as unknown as K1]))
1345 }
1346 }
1347 return out
1348 }
1349)
1350
1351/**
1352 * Merges two records, preserving only the entries that are unique to each record.
1353 * Keys that exist in both records are excluded from the result.
1354 *
1355 * **Example** (Keeping keys unique to each record)
1356 *
1357 * ```ts import.meta.vitest
1358 * import { Record } from "effect"
1359 *
1360 * Record.difference({ a: 1, b: 2 }, { b: 3, c: 4 }) // => { a: 1, c: 4 }
1361 * ```
1362 *
1363 * @category combining
1364 * @since 2.0.0
1365 */
1366export const difference: {
1367 <K1 extends string, B>(
1368 that: ReadonlyRecord<K1, B>
1369 ): <K0 extends string, A>(self: ReadonlyRecord<K0, A>) => Record<K0 | K1, A | B>
1370 <K0 extends string, A, K1 extends string, B>(
1371 self: ReadonlyRecord<K0, A>,
1372 that: ReadonlyRecord<K1, B>
1373 ): Record<K0 | K1, A | B>
1374} = dual(2, <K0 extends string, A, K1 extends string, B>(
1375 self: ReadonlyRecord<K0, A>,
1376 that: ReadonlyRecord<K1, B>
1377): Record<K0 | K1, A | B> => {
1378 if (isEmptyRecord(self)) {
1379 return { ...that } as any
1380 }
1381 if (isEmptyRecord(that)) {
1382 return { ...self } as any
1383 }
1384 const out = {} as Record<K0 | K1, A | B>
1385 for (const key of keys(self)) {
1386 if (!has(that, key as any)) {
1387 InternalRecord.assignProperty(out, key, self[key])
1388 }
1389 }
1390 for (const key of keys(that)) {
1391 if (!has(self, key as any)) {
1392 InternalRecord.assignProperty(out, key, that[key])
1393 }
1394 }
1395 return out
1396})
1397
1398/**
1399 * Create an `Equivalence` for records using the provided `Equivalence` for values.
1400 * Two records are considered equivalent if they have the same keys and their corresponding values are equivalent.
1401 *
1402 * **Example** (Comparing records with a value equivalence)
1403 *
1404 * ```ts import.meta.vitest
1405 * import { Equal, Record } from "effect"
1406 *
1407 * const recordEquivalence = Record.makeEquivalence(Equal.asEquivalence<number>())
1408 *
1409 * recordEquivalence({ a: 1, b: 2 }, { a: 1, b: 2 }) // => true
1410 * recordEquivalence({ a: 1, b: 2 }, { a: 1, b: 3 }) // => false
1411 * ```
1412 *
1413 * @category instances
1414 * @since 4.0.0
1415 */
1416export const makeEquivalence = <K extends string, A>(
1417 equivalence: Equivalence<A>
1418): Equivalence<ReadonlyRecord<K, A>> => {
1419 const is = isSubrecordBy(equivalence)
1420 return (self, that) => is(self, that) && is(that, self)
1421}
1422
1423/**
1424 * Create a non-empty record from a single element.
1425 *
1426 * **Example** (Creating a singleton record)
1427 *
1428 * ```ts import.meta.vitest
1429 * import { Record } from "effect"
1430 *
1431 * Record.singleton("a", 1) // => { a: 1 }
1432 * ```
1433 *
1434 * @category constructors
1435 * @since 2.0.0
1436 */
1437export const singleton = <K extends string | symbol, A>(key: K, value: A): Record<K, A> => ({
1438 [key]: value
1439} as any)
1440
1441/**
1442 * Creates a `Reducer` for combining `Record`s using union, with values for keys that exist in both records combined
1443 * using the provided `Combiner`.
1444 *
1445 * **When to use**
1446 *
1447 * Use to build a reusable reducer for accumulating many records into one
1448 * union-shaped record, preserving keys from every input and combining
1449 * overlapping values with the supplied combiner.
1450 *
1451 * **Details**
1452 *
1453 * The returned reducer uses `Record.union` for combine and an empty record as
1454 * `initialValue`, so the default `combineAll` folds from `{}` and accumulates
1455 * keys from each input record.
1456 *
1457 * @see {@link union} for one-off record merging with the same union semantics
1458 * @see {@link makeReducerIntersection} for a reducer that keeps only keys present on both sides
1459 *
1460 * @category combining
1461 * @since 4.0.0
1462 */
1463export function makeReducerUnion<K extends string, A>(combiner: Combiner.Combiner<A>): Reducer.Reducer<Record<K, A>> {
1464 return Reducer.make<Record<K, A>>(
1465 (self, that) => union(self, that, combiner.combine),
1466 {} as Record<K, A>
1467 )
1468}
1469
1470/**
1471 * Creates a `Reducer` whose `combine` operation intersects two records and
1472 * combines values for keys present in both records.
1473 *
1474 * **When to use**
1475 *
1476 * Use to build a `Reducer` that combines records by retaining only keys shared
1477 * by both inputs and combining matching values with a `Combiner`.
1478 *
1479 * **Gotchas**
1480 *
1481 * The reducer's `initialValue` is an empty record. Because intersection with
1482 * an empty record is empty, the default `combineAll` folds from `{}` and
1483 * therefore produces `{}` for ordinary non-empty inputs.
1484 *
1485 * @see {@link makeReducerUnion} for a reducer that preserves keys from either input record
1486 * @see {@link intersection} for applying the shared-key merge to one pair of records
1487 *
1488 * @category combining
1489 * @since 4.0.0
1490 */
1491export function makeReducerIntersection<K extends string, A>(
1492 combiner: Combiner.Combiner<A>
1493): Reducer.Reducer<Record<K, A>> {
1494 return Reducer.make(
1495 (self, that) => intersection(self, that, combiner.combine) as any,
1496 {} as Record<K, A>
1497 )
1498}
1499
1500/**
1501 * Returns the first entry that satisfies the specified
1502 * predicate, or `None` if no such entry exists.
1503 *
1504 * **Example** (Finding the first matching entry)
1505 *
1506 * ```ts import.meta.vitest
1507 * import { Option, Record } from "effect"
1508 *
1509 * const record = { a: 1, b: 2, c: 3 }
1510 * Record.findFirst(
1511 * record,
1512 * (value, key) => value > 1 && key !== "b"
1513 * ) // => Option.some(["c", 3])
1514 * ```
1515 *
1516 * @category searching
1517 * @since 3.14.0
1518 */
1519export const findFirst: {
1520 <K extends string | symbol, V, V2 extends V>(
1521 refinement: (value: NoInfer<V>, key: NoInfer<K>) => value is V2
1522 ): (self: ReadonlyRecord<K, V>) => Option.Option<[K, V2]>
1523 <K extends string | symbol, V>(
1524 predicate: (value: NoInfer<V>, key: NoInfer<K>) => boolean
1525 ): (self: ReadonlyRecord<K, V>) => Option.Option<[K, V]>
1526 <K extends string | symbol, V, V2 extends V>(
1527 self: ReadonlyRecord<K, V>,
1528 refinement: (value: NoInfer<V>, key: NoInfer<K>) => value is V2
1529 ): Option.Option<[K, V2]>
1530 <K extends string | symbol, V>(
1531 self: ReadonlyRecord<K, V>,
1532 predicate: (value: NoInfer<V>, key: NoInfer<K>) => boolean
1533 ): Option.Option<[K, V]>
1534} = dual(
1535 2,
1536 <K extends string | symbol, V>(
1537 self: ReadonlyRecord<K, V>,
1538 f: (value: V, key: K) => boolean
1539 ): Option.Option<[K, V]> => {
1540 const k = keys(self)
1541 for (let i = 0; i < k.length; i++) {
1542 const key = k[i]
1543 if (f(self[key], key)) {
1544 return Option.some([key, self[key]])
1545 }
1546 }
1547 return Option.none()
1548 }
1549)