1/**
2 * Stores key/value entries in a mutable hash map.
3 *
4 * `MutableHashMap` updates the same collection in place and supports fast
5 * lookup, insertion, removal, clearing, and iteration. It combines a native
6 * `Map` for ordinary JavaScript keys with hash buckets for keys that implement
7 * Effect `Equal` and `Hash`, so callers can mix reference-based and structural
8 * lookup in the same collection.
9 *
10 * @since 2.0.0
11 */
12import type { NonEmptyArray } from "./Array.ts"
13import * as Equal from "./Equal.ts"
14import { format } from "./Formatter.ts"
15import { dual } from "./Function.ts"
16import * as Hash from "./Hash.ts"
17import { type Inspectable, NodeInspectSymbol, toJson } from "./Inspectable.ts"
18import * as Option from "./Option.ts"
19import type { Pipeable } from "./Pipeable.ts"
20import { pipeArguments } from "./Pipeable.ts"
21import { hasProperty } from "./Predicate.ts"
22
23const TypeId = "~effect/MutableHashMap"
24
25/**
26 * A mutable hash map that stores key-value pairs and supports both referential
27 * and Effect structural equality.
28 *
29 * **When to use**
30 *
31 * Use as a mutable key-value map when in-place updates are acceptable and keys
32 * may rely on Effect structural equality.
33 *
34 * **Details**
35 *
36 * Operations mutate the map in place. Keys that implement `Equal` / `Hash` can
37 * be looked up structurally; other keys use normal JavaScript reference or
38 * primitive equality.
39 *
40 * **Example** (Using a mutable hash map)
41 *
42 * ```ts import.meta.vitest
43 * import { MutableHashMap } from "effect"
44 *
45 * // Create a mutable hash map with string keys and number values
46 * const map: MutableHashMap.MutableHashMap<string, number> = MutableHashMap
47 * .empty()
48 *
49 * // Add some data
50 * MutableHashMap.set(map, "count", 42)
51 * MutableHashMap.set(map, "total", 100)
52 *
53 * Array.from(map) // => [["count", 42], ["total", 100]]
54 * ```
55 *
56 * @see {@link empty} for creating an empty mutable hash map
57 * @see {@link get} for reading values by key
58 * @see {@link set} for mutating entries by key
59 *
60 * @category models
61 * @since 2.0.0
62 */
63export interface MutableHashMap<out K, out V> extends Iterable<[K, V]>, Pipeable, Inspectable {
64 readonly [TypeId]: typeof TypeId
65 readonly backing: Map<K, V>
66 readonly buckets: Map<number, NonEmptyArray<K>>
67}
68
69/**
70 * Checks whether the specified value is a `MutableHashMap`, `false` otherwise.
71 *
72 * **When to use**
73 *
74 * Use to narrow an unknown value before treating it as a mutable hash map.
75 *
76 * **Details**
77 *
78 * The check looks for the `MutableHashMap` runtime marker.
79 *
80 * **Gotchas**
81 *
82 * The check does not validate the key or value types carried by the map.
83 *
84 * @see {@link MutableHashMap} for the mutable hash map interface
85 *
86 * @category guards
87 * @since 4.0.0
88 */
89export const isMutableHashMap = <K, V>(value: unknown): value is MutableHashMap<K, V> => hasProperty(value, TypeId)
90
91const MutableHashMapProto: Omit<MutableHashMap<unknown, unknown>, "backing" | "buckets" | "bucketsSize"> = {
92 [TypeId]: TypeId,
93 [Symbol.iterator](this: MutableHashMap<unknown, unknown>): Iterator<[unknown, unknown]> {
94 return this.backing[Symbol.iterator]()
95 },
96 toString() {
97 return `MutableHashMap(${format(Array.from(this))})`
98 },
99 toJSON() {
100 return {
101 _id: "MutableHashMap",
102 values: toJson(Array.from(this))
103 }
104 },
105 [NodeInspectSymbol]() {
106 return this.toJSON()
107 },
108 pipe() {
109 return pipeArguments(this, arguments)
110 }
111}
112
113/**
114 * Creates an empty MutableHashMap.
115 *
116 * **When to use**
117 *
118 * Use to create a fresh mutable map before adding entries over time.
119 *
120 * **Details**
121 *
122 * Each call returns a new empty map instance.
123 *
124 * **Example** (Creating an empty map)
125 *
126 * ```ts import.meta.vitest
127 * import { MutableHashMap } from "effect"
128 *
129 * const map = MutableHashMap.empty<string, number>()
130 *
131 * // Add some entries
132 * MutableHashMap.set(map, "key1", 42)
133 * MutableHashMap.set(map, "key2", 100)
134 *
135 * MutableHashMap.size(map) // => 2
136 * ```
137 *
138 * @see {@link make} for creating a map from explicit entries
139 * @see {@link fromIterable} for creating a map from an iterable of entries
140 *
141 * @category constructors
142 * @since 2.0.0
143 */
144export const empty = <K, V>(): MutableHashMap<K, V> => {
145 const self = Object.create(MutableHashMapProto)
146 self.backing = new Map()
147 self.buckets = new Map()
148 return self
149}
150
151/**
152 * Creates a MutableHashMap from a variable number of key-value pairs.
153 *
154 * **When to use**
155 *
156 * Use to create a mutable hash map from explicit entries known at the call site.
157 *
158 * **Example** (Creating a map from entries)
159 *
160 * ```ts import.meta.vitest
161 * import { MutableHashMap, Option } from "effect"
162 *
163 * const map = MutableHashMap.make(
164 * ["key1", 42],
165 * ["key2", 100],
166 * ["key3", 200]
167 * )
168 *
169 * MutableHashMap.get(map, "key1") // => Option.some(42)
170 * MutableHashMap.size(map) // => 3
171 * ```
172 *
173 * @see {@link empty} for creating an empty map
174 * @see {@link fromIterable} for creating a map from an iterable of entries
175 *
176 * @category constructors
177 * @since 2.0.0
178 */
179export const make: <Entries extends Array<readonly [any, any]>>(
180 ...entries: Entries
181) => MutableHashMap<
182 Entries[number] extends readonly [infer K, any] ? K : never,
183 Entries[number] extends readonly [any, infer V] ? V : never
184> = (...entries) => fromIterable(entries)
185
186/**
187 * Creates a MutableHashMap from an iterable collection of key-value pairs.
188 *
189 * **When to use**
190 *
191 * Use to create a mutable hash map from an existing iterable of entries.
192 *
193 * **Example** (Creating a map from an iterable)
194 *
195 * ```ts import.meta.vitest
196 * import { MutableHashMap, Option } from "effect"
197 *
198 * const entries = [
199 * ["apple", 1],
200 * ["banana", 2],
201 * ["cherry", 3]
202 * ] as const
203 *
204 * const map = MutableHashMap.fromIterable(entries)
205 *
206 * MutableHashMap.get(map, "banana") // => Option.some(2)
207 * MutableHashMap.size(map) // => 3
208 *
209 * // Works with any iterable
210 * const fromMap = MutableHashMap.fromIterable(new Map([["x", 10], ["y", 20]]))
211 * MutableHashMap.get(fromMap, "x") // => Option.some(10)
212 * ```
213 *
214 * @see {@link make} for creating a map from explicit entries
215 * @see {@link empty} for creating an empty map
216 *
217 * @category constructors
218 * @since 2.0.0
219 */
220export const fromIterable = <K, V>(entries: Iterable<readonly [K, V]>): MutableHashMap<K, V> => {
221 const self = empty<K, V>()
222 for (const [key, value] of entries) {
223 set(self, key, value)
224 }
225 return self
226}
227
228/**
229 * Looks up a key in the `MutableHashMap` safely.
230 *
231 * **When to use**
232 *
233 * Use to safely read a `MutableHashMap` value for a key as an `Option`.
234 *
235 * **Details**
236 *
237 * Returns `Some(value)` when an equal key is present and `None` when the key is
238 * absent.
239 *
240 * **Example** (Getting a value)
241 *
242 * ```ts import.meta.vitest
243 * import { MutableHashMap, Option } from "effect"
244 *
245 * const map = MutableHashMap.make(["key1", 42], ["key2", 100])
246 *
247 * MutableHashMap.get(map, "key1") // => Option.some(42)
248 * MutableHashMap.get(map, "key3") // => Option.none()
249 *
250 * // Pipe-able version
251 * MutableHashMap.get("key1")(map) // => Option.some(42)
252 * ```
253 *
254 * @see {@link has} for checking only whether a key is present
255 * @see {@link set} for inserting or replacing a value by key
256 *
257 * @category getters
258 * @since 2.0.0
259 */
260export const get: {
261 <K>(key: K): <V>(self: MutableHashMap<K, V>) => Option.Option<V>
262 <K, V>(self: MutableHashMap<K, V>, key: K): Option.Option<V>
263} = dual<
264 <K>(key: K) => <V>(self: MutableHashMap<K, V>) => Option.Option<V>,
265 <K, V>(self: MutableHashMap<K, V>, key: K) => Option.Option<V>
266>(2, <K, V>(self: MutableHashMap<K, V>, key: K): Option.Option<V> => {
267 if (self.backing.has(key)) {
268 return Option.some(self.backing.get(key)!)
269 } else if (isSimpleKey(key)) {
270 return Option.none()
271 }
272 const hash = Hash.hash(key)
273 const bucket = self.buckets.get(hash)
274 if (bucket === undefined) {
275 return Option.none()
276 }
277 return getFromBucket(self, bucket, key)
278})
279
280const isSimpleKey = (u: unknown): boolean => typeof u !== "object" && typeof u !== "function"
281
282/**
283 * Returns an iterable over the keys in the `MutableHashMap`.
284 *
285 * **When to use**
286 *
287 * Use to iterate over the keys currently stored in a mutable hash map.
288 *
289 * **Example** (Reading keys)
290 *
291 * ```ts import.meta.vitest
292 * import { MutableHashMap } from "effect"
293 *
294 * const map = MutableHashMap.make(
295 * ["apple", 1],
296 * ["banana", 2],
297 * ["cherry", 3]
298 * )
299 *
300 * Array.from(MutableHashMap.keys(map)) // => ["apple", "banana", "cherry"]
301 * ```
302 *
303 * @see {@link values} for iterating over stored values
304 * @see {@link has} for checking one key without iterating
305 *
306 * @category getters
307 * @since 3.8.0
308 */
309export const keys = <K, V>(self: MutableHashMap<K, V>): Iterable<K> => self.backing.keys()
310
311/**
312 * Returns an iterable over the values in the `MutableHashMap`.
313 *
314 * **When to use**
315 *
316 * Use to iterate over the values currently stored in a mutable hash map.
317 *
318 * **Example** (Reading values)
319 *
320 * ```ts import.meta.vitest
321 * import { MutableHashMap } from "effect"
322 *
323 * const map = MutableHashMap.make(
324 * ["apple", 1],
325 * ["banana", 2],
326 * ["cherry", 3]
327 * )
328 *
329 * const allValues = Array.from(MutableHashMap.values(map)) // => [1, 2, 3]
330 *
331 * // Useful for calculations
332 * allValues.reduce((sum, value) => sum + value, 0) // => 6
333 *
334 * // Filter values
335 * allValues.filter((value) => value > 1) // => [2, 3]
336 * ```
337 *
338 * @see {@link keys} for iterating over stored keys
339 *
340 * @category getters
341 * @since 3.8.0
342 */
343export const values = <K, V>(self: MutableHashMap<K, V>): Iterable<V> => self.backing.values()
344
345const getFromBucket = <K, V>(
346 self: MutableHashMap<K, V>,
347 bucket: NonEmptyArray<K>,
348 key: K
349): Option.Option<V> => {
350 for (let i = 0, len = bucket.length; i < len; i++) {
351 if (Equal.equals(key, bucket[i])) {
352 const refKey = bucket[i]
353 return Option.some(self.backing.get(refKey)!)
354 }
355 }
356 return Option.none()
357}
358
359/**
360 * Checks whether the MutableHashMap contains the specified key.
361 *
362 * **When to use**
363 *
364 * Use to test whether a key is present in a `MutableHashMap` without reading
365 * its value.
366 *
367 * **Example** (Checking for a key)
368 *
369 * ```ts import.meta.vitest
370 * import { MutableHashMap } from "effect"
371 *
372 * const map = MutableHashMap.make(["key1", 42], ["key2", 100])
373 *
374 * MutableHashMap.has(map, "key1") // => true
375 * MutableHashMap.has(map, "key3") // => false
376 *
377 * // Pipe-able version
378 * MutableHashMap.has("key1")(map) // => true
379 * ```
380 *
381 * @see {@link get} for reading the value as an `Option`
382 *
383 * @category predicates
384 * @since 2.0.0
385 */
386export const has: {
387 <K>(key: K): <V>(self: MutableHashMap<K, V>) => boolean
388 <K, V>(self: MutableHashMap<K, V>, key: K): boolean
389} = dual<
390 <K>(key: K) => <V>(self: MutableHashMap<K, V>) => boolean,
391 <K, V>(self: MutableHashMap<K, V>, key: K) => boolean
392>(2, (self, key) => Option.isSome(get(self, key)))
393
394/**
395 * Sets a key-value pair in the MutableHashMap, mutating the map in place.
396 * If the key already exists, its value is updated.
397 *
398 * **When to use**
399 *
400 * Use to insert a new `MutableHashMap` entry or replace an existing entry in
401 * place.
402 *
403 * **Example** (Setting key-value pairs)
404 *
405 * ```ts import.meta.vitest
406 * import { MutableHashMap, Option } from "effect"
407 *
408 * const map = MutableHashMap.empty<string, number>()
409 *
410 * // Add new entries
411 * MutableHashMap.set(map, "key1", 42)
412 * MutableHashMap.set(map, "key2", 100)
413 *
414 * MutableHashMap.get(map, "key1") // => Option.some(42)
415 * MutableHashMap.size(map) // => 2
416 *
417 * // Update existing entry
418 * MutableHashMap.set(map, "key1", 999)
419 * MutableHashMap.get(map, "key1") // => Option.some(999)
420 *
421 * // Pipe-able version
422 * MutableHashMap.set("key3", 300)(map)
423 * MutableHashMap.size(map) // => 3
424 * ```
425 *
426 * @see {@link modify} for updating an existing value with a function
427 * @see {@link modifyAt} for setting or removing based on the current optional value
428 * @see {@link remove} for deleting an entry by key
429 *
430 * @category mutations
431 * @since 2.0.0
432 */
433export const set: {
434 <K, V>(key: K, value: V): (self: MutableHashMap<K, V>) => MutableHashMap<K, V>
435 <K, V>(self: MutableHashMap<K, V>, key: K, value: V): MutableHashMap<K, V>
436} = dual<
437 <K, V>(key: K, value: V) => (self: MutableHashMap<K, V>) => MutableHashMap<K, V>,
438 <K, V>(self: MutableHashMap<K, V>, key: K, value: V) => MutableHashMap<K, V>
439>(3, <K, V>(self: MutableHashMap<K, V>, key: K, value: V) => {
440 if (self.backing.has(key) || isSimpleKey(key)) {
441 self.backing.set(key, value)
442 return self
443 }
444 const hash = Hash.hash(key)
445 const bucket = self.buckets.get(hash)
446 if (bucket === undefined) {
447 self.buckets.set(hash, [key])
448 self.backing.set(key, value)
449 return self
450 }
451
452 let refKey = getRefKey(bucket, key)
453 if (refKey === undefined) {
454 bucket.push(key)
455 refKey = key
456 }
457 self.backing.set(refKey, value)
458 return self
459})
460
461const getRefKey = <K>(
462 bucket: NonEmptyArray<K>,
463 key: K
464) => {
465 for (let i = 0, len = bucket.length; i < len; i++) {
466 if (Equal.equals(key, bucket[i])) {
467 return bucket[i]
468 }
469 }
470}
471
472/**
473 * Updates the value of the specified key within the MutableHashMap if it exists.
474 * If the key doesn't exist, the map remains unchanged.
475 *
476 * **When to use**
477 *
478 * Use to transform an existing `MutableHashMap` value in place without
479 * inserting missing keys.
480 *
481 * **Example** (Modifying existing values)
482 *
483 * ```ts import.meta.vitest
484 * import { MutableHashMap, Option } from "effect"
485 *
486 * const map = MutableHashMap.make(["count", 5], ["total", 100])
487 *
488 * // Increment existing value
489 * MutableHashMap.modify(map, "count", (n) => n + 1)
490 * MutableHashMap.get(map, "count") // => Option.some(6)
491 *
492 * // Double existing value
493 * MutableHashMap.modify(map, "total", (n) => n * 2)
494 * MutableHashMap.get(map, "total") // => Option.some(200)
495 *
496 * // Try to modify non-existent key (no effect)
497 * MutableHashMap.modify(map, "missing", (n) => n + 1)
498 * MutableHashMap.has(map, "missing") // => false
499 *
500 * // Pipe-able version
501 * MutableHashMap.modify("count", (n: number) => n + 1)(map)
502 * MutableHashMap.get(map, "count") // => Option.some(7)
503 * ```
504 *
505 * @see {@link set} for inserting or replacing a value directly
506 * @see {@link modifyAt} for handling both missing and existing keys
507 *
508 * @category mutations
509 * @since 2.0.0
510 */
511export const modify: {
512 <K, V>(key: K, f: (v: V) => V): (self: MutableHashMap<K, V>) => MutableHashMap<K, V>
513 <K, V>(self: MutableHashMap<K, V>, key: K, f: (v: V) => V): MutableHashMap<K, V>
514} = dual<
515 <K, V>(key: K, f: (v: V) => V) => (self: MutableHashMap<K, V>) => MutableHashMap<K, V>,
516 <K, V>(self: MutableHashMap<K, V>, key: K, f: (v: V) => V) => MutableHashMap<K, V>
517>(3, <K, V>(self: MutableHashMap<K, V>, key: K, f: (v: V) => V) => {
518 const hasKey = self.backing.has(key)
519 if (hasKey || isSimpleKey(key)) {
520 if (hasKey) {
521 self.backing.set(key, f(self.backing.get(key)!))
522 }
523 return self
524 }
525 const hash = Hash.hash(key)
526 const bucket = self.buckets.get(hash)
527 if (bucket === undefined) {
528 return self
529 }
530
531 const refKey = getRefKey(bucket, key)
532 if (refKey === undefined) {
533 return self
534 }
535 self.backing.set(refKey, f(self.backing.get(refKey)!))
536 return self
537})
538
539/**
540 * Updates or removes the specified key using a function from the current
541 * optional value to the next optional value.
542 *
543 * **When to use**
544 *
545 * Use to decide whether to insert, update, or remove a key based on its current
546 * optional value.
547 *
548 * **Example** (Updating or removing a key)
549 *
550 * ```ts import.meta.vitest
551 * import { MutableHashMap, Option } from "effect"
552 *
553 * const map = MutableHashMap.make(["count", 5])
554 *
555 * // Update existing key
556 * MutableHashMap.modifyAt(
557 * map,
558 * "count",
559 * (option) => Option.map(option, (n) => n * 2)
560 * )
561 * MutableHashMap.get(map, "count") // => Option.some(10)
562 *
563 * // Add new key
564 * MutableHashMap.modifyAt(
565 * map,
566 * "new",
567 * (option) => Option.isNone(option) ? Option.some(42) : option
568 * )
569 * MutableHashMap.get(map, "new") // => Option.some(42)
570 *
571 * // Remove key by returning None
572 * MutableHashMap.modifyAt(map, "count", () => Option.none())
573 * MutableHashMap.get(map, "count") // => Option.none()
574 *
575 * // Conditional update
576 * MutableHashMap.modifyAt(
577 * map,
578 * "new",
579 * (option) => Option.filter(option, (n) => n > 50) // Remove if <= 50
580 * )
581 * MutableHashMap.get(map, "new") // => Option.none()
582 * ```
583 *
584 * @see {@link modify} for updating only when the key already exists
585 * @see {@link set} for inserting or replacing directly
586 * @see {@link remove} for deleting directly
587 *
588 * @category mutations
589 * @since 2.0.0
590 */
591export const modifyAt: {
592 <K, V>(key: K, f: (value: Option.Option<V>) => Option.Option<V>): (self: MutableHashMap<K, V>) => MutableHashMap<K, V>
593 <K, V>(self: MutableHashMap<K, V>, key: K, f: (value: Option.Option<V>) => Option.Option<V>): MutableHashMap<K, V>
594} = dual<
595 <K, V>(
596 key: K,
597 f: (value: Option.Option<V>) => Option.Option<V>
598 ) => (self: MutableHashMap<K, V>) => MutableHashMap<K, V>,
599 <K, V>(
600 self: MutableHashMap<K, V>,
601 key: K,
602 f: (value: Option.Option<V>) => Option.Option<V>
603 ) => MutableHashMap<K, V>
604>(3, (self, key, f) => {
605 const current = get(self, key)
606 const result = f(current)
607 if (Option.isNone(result)) {
608 if (Option.isSome(current)) {
609 remove(self, key)
610 }
611 return self
612 }
613 set(self, key, result.value)
614 return self
615})
616
617/**
618 * Removes the specified key from the MutableHashMap, mutating the map in place.
619 * If the key doesn't exist, the map remains unchanged.
620 *
621 * **When to use**
622 *
623 * Use to delete one key from a mutable hash map in place.
624 *
625 * **Example** (Removing a key)
626 *
627 * ```ts import.meta.vitest
628 * import { MutableHashMap } from "effect"
629 *
630 * const map = MutableHashMap.make(
631 * ["key1", 42],
632 * ["key2", 100],
633 * ["key3", 200]
634 * )
635 *
636 * MutableHashMap.size(map) // => 3
637 *
638 * // Remove existing key
639 * MutableHashMap.remove(map, "key2")
640 * MutableHashMap.size(map) // => 2
641 * MutableHashMap.has(map, "key2") // => false
642 *
643 * // Remove non-existent key (no effect)
644 * MutableHashMap.remove(map, "nonexistent")
645 * MutableHashMap.size(map) // => 2
646 *
647 * // Pipe-able version
648 * MutableHashMap.remove("key1")(map)
649 * MutableHashMap.size(map) // => 1
650 * ```
651 *
652 * @see {@link clear} for removing all entries
653 * @see {@link modifyAt} for conditionally removing based on the current value
654 *
655 * @category mutations
656 * @since 2.0.0
657 */
658export const remove: {
659 <K>(key: K): <V>(self: MutableHashMap<K, V>) => MutableHashMap<K, V>
660 <K, V>(self: MutableHashMap<K, V>, key: K): MutableHashMap<K, V>
661} = dual<
662 <K>(key: K) => <V>(self: MutableHashMap<K, V>) => MutableHashMap<K, V>,
663 <K, V>(self: MutableHashMap<K, V>, key: K) => MutableHashMap<K, V>
664>(2, <K, V>(self: MutableHashMap<K, V>, key_: K) => {
665 if (isSimpleKey(key_)) {
666 self.backing.delete(key_)
667 return self
668 }
669
670 const hash = Hash.hash(key_)
671 const bucket = self.buckets.get(hash)
672 if (bucket === undefined) {
673 return self
674 }
675 for (let i = 0, len = bucket.length; i < len; i++) {
676 const bkey = bucket[i]
677 if (bkey === key_ || Equal.equals(key_, bkey)) {
678 self.backing.delete(bkey)
679 bucket.splice(i, 1)
680 break
681 }
682 }
683 if (bucket.length === 0) {
684 self.buckets.delete(hash)
685 }
686 return self
687})
688
689/**
690 * Removes all key-value pairs from the MutableHashMap, mutating the map in place.
691 * The map becomes empty after this operation.
692 *
693 * **When to use**
694 *
695 * Use to empty a mutable hash map while keeping the same map instance.
696 *
697 * **Example** (Clearing all entries)
698 *
699 * ```ts import.meta.vitest
700 * import { MutableHashMap } from "effect"
701 *
702 * const map = MutableHashMap.make(
703 * ["key1", 42],
704 * ["key2", 100],
705 * ["key3", 200]
706 * )
707 *
708 * MutableHashMap.size(map) // => 3
709 *
710 * // Clear all entries
711 * MutableHashMap.clear(map)
712 *
713 * MutableHashMap.size(map) // => 0
714 * MutableHashMap.has(map, "key1") // => false
715 *
716 * // Can still add new entries after clearing
717 * MutableHashMap.set(map, "new", 999)
718 * Array.from(map) // => [["new", 999]]
719 * ```
720 *
721 * @see {@link remove} for deleting one key
722 * @see {@link empty} for creating a fresh empty map
723 *
724 * @category mutations
725 * @since 2.0.0
726 */
727export const clear = <K, V>(self: MutableHashMap<K, V>) => {
728 self.backing.clear()
729 self.buckets.clear()
730 return self
731}
732
733/**
734 * Returns the number of key-value pairs in the MutableHashMap.
735 *
736 * **When to use**
737 *
738 * Use to read how many entries are currently stored in the mutable hash map.
739 *
740 * **Example** (Checking map size)
741 *
742 * ```ts import.meta.vitest
743 * import { MutableHashMap } from "effect"
744 *
745 * const map = MutableHashMap.empty<string, number>()
746 * MutableHashMap.size(map) // => 0
747 *
748 * MutableHashMap.set(map, "key1", 42)
749 * MutableHashMap.set(map, "key2", 100)
750 * MutableHashMap.size(map) // => 2
751 *
752 * MutableHashMap.remove(map, "key1")
753 * MutableHashMap.size(map) // => 1
754 *
755 * MutableHashMap.clear(map)
756 * MutableHashMap.size(map) // => 0
757 * ```
758 *
759 * @see {@link isEmpty} for checking whether the map has no entries
760 *
761 * @category getters
762 * @since 2.0.0
763 */
764export const size = <K, V>(self: MutableHashMap<K, V>): number => self.backing.size
765
766/**
767 * Returns `true` when the `MutableHashMap` contains no key-value pairs.
768 *
769 * **When to use**
770 *
771 * Use to branch on whether a mutable map currently has any entries.
772 *
773 * @see {@link size} for reading the exact number of entries
774 *
775 * @category predicates
776 * @since 2.0.0
777 */
778export const isEmpty = <K, V>(self: MutableHashMap<K, V>): boolean => self.backing.size === 0
779
780/**
781 * Runs a callback for each key-value pair in the `MutableHashMap`.
782 *
783 * **When to use**
784 *
785 * Use to run a synchronous side-effecting callback for every key-value pair in
786 * an existing mutable map.
787 *
788 * **Details**
789 *
790 * Iteration follows the backing map's order. The callback receives the value
791 * first and the key second, matching `Map.prototype.forEach`.
792 *
793 * @see {@link keys} for iterating only keys
794 * @see {@link values} for iterating only values
795 *
796 * @category traversing
797 * @since 2.0.0
798 */
799export const forEach: {
800 <K, V>(f: (value: V, key: K) => void): (self: MutableHashMap<K, V>) => void
801 <K, V>(self: MutableHashMap<K, V>, f: (value: V, key: K) => void): void
802} = dual(2, <K, V>(self: MutableHashMap<K, V>, f: (value: V, key: K) => void) => {
803 self.backing.forEach(f)
804})