| 1 | /** |
| 2 | * Stores key/value entries in an immutable hash map. |
| 3 | * |
| 4 | * A `HashMap<Key, Value>` hashes keys and resolves matches with Effect's |
| 5 | * structural equality rules. Lookup, insertion, removal, and transformation |
| 6 | * operations return new maps, while temporary mutation helpers support efficient |
| 7 | * batch updates. This module also includes constructors, iteration, conversion, |
| 8 | * mapping, filtering, and reducing helpers. |
| 9 | * |
| 10 | * @since 2.0.0 |
| 11 | */ |
| 12 | |
| 13 | import type { Equal } from "./Equal.ts" |
| 14 | import type { Inspectable } from "./Inspectable.ts" |
| 15 | import * as internal from "./internal/hashMap.ts" |
| 16 | import type { Option } from "./Option.ts" |
| 17 | import type { Pipeable } from "./Pipeable.ts" |
| 18 | import type { Result } from "./Result.ts" |
| 19 | import type { NoInfer } from "./Types.ts" |
| 20 | |
| 21 | const TypeId = internal.HashMapTypeId |
| 22 | |
| 23 | /** |
| 24 | * A HashMap is an immutable key-value data structure that provides efficient lookup, |
| 25 | * insertion, and deletion operations. It uses a Hash Array Mapped Trie (HAMT) internally |
| 26 | * for structural sharing and optimal performance. |
| 27 | * |
| 28 | * **Example** (Using basic HashMap operations) |
| 29 | * |
| 30 | * ```ts import.meta.vitest |
| 31 | * import { HashMap, Option } from "effect" |
| 32 | * |
| 33 | * // Create a HashMap |
| 34 | * const map = HashMap.make(["a", 1], ["b", 2], ["c", 3]) |
| 35 | * |
| 36 | * // Access values |
| 37 | * HashMap.get(map, "a") // => Option.some(1) |
| 38 | * HashMap.get(map, "d") // => Option.none() |
| 39 | * |
| 40 | * // Check if key exists |
| 41 | * HashMap.has(map, "b") // => true |
| 42 | * |
| 43 | * // Add/update values (returns new HashMap) |
| 44 | * HashMap.set(map, "d", 4) // => HashMap.make(["a", 1], ["b", 2], ["c", 3], ["d", 4]) |
| 45 | * ``` |
| 46 | * |
| 47 | * @category models |
| 48 | * @since 2.0.0 |
| 49 | */ |
| 50 | export interface HashMap<out Key, out Value> extends Iterable<[Key, Value]>, Equal, Pipeable, Inspectable { |
| 51 | readonly [TypeId]: typeof TypeId |
| 52 | } |
| 53 | |
| 54 | /** |
| 55 | * The HashMap namespace contains type-level utilities and helper types |
| 56 | * for working with HashMap instances. |
| 57 | * |
| 58 | * **Example** (Extracting HashMap types) |
| 59 | * |
| 60 | * ```ts import.meta.vitest |
| 61 | * import { HashMap } from "effect" |
| 62 | * |
| 63 | * // Create a concrete HashMap for type extraction |
| 64 | * const inventory = HashMap.make( |
| 65 | * ["laptop", { quantity: 5, price: 999 }], |
| 66 | * ["mouse", { quantity: 20, price: 29 }] |
| 67 | * ) |
| 68 | * |
| 69 | * // Extract types for reuse |
| 70 | * type ProductId = HashMap.HashMap.Key<typeof inventory> // string |
| 71 | * type Product = HashMap.HashMap.Value<typeof inventory> // { quantity: number, price: number } |
| 72 | * type InventoryEntry = HashMap.HashMap.Entry<typeof inventory> // [string, Product] |
| 73 | * |
| 74 | * // Use extracted types in functions |
| 75 | * const updateInventory = (id: ProductId, product: Product) => |
| 76 | * HashMap.set(inventory, id, product) |
| 77 | * |
| 78 | * const processEntry = ([id, product]: InventoryEntry) => |
| 79 | * `${id}: ${product.quantity} @ $${product.price}` |
| 80 | * |
| 81 | * // Example of extracted types in action |
| 82 | * const newProduct: Product = { quantity: 10, price: 199 } |
| 83 | * const updatedInventory = updateInventory("tablet", newProduct) |
| 84 | * processEntry(["tablet", newProduct]) // => "tablet: 10 @ $199" |
| 85 | * updatedInventory // => HashMap.make(["laptop", { quantity: 5, price: 999 }], ["mouse", { quantity: 20, price: 29 }], ["tablet", newProduct]) |
| 86 | * ``` |
| 87 | * |
| 88 | * @since 2.0.0 |
| 89 | */ |
| 90 | export declare namespace HashMap { |
| 91 | /** |
| 92 | * A function that updates a value based on its current state. |
| 93 | * Takes an Option representing the current value and returns an Option |
| 94 | * representing the new value. |
| 95 | * |
| 96 | * **Example** (Updating values from Options) |
| 97 | * |
| 98 | * ```ts import.meta.vitest |
| 99 | * import { HashMap, Option } from "effect" |
| 100 | * |
| 101 | * const map = HashMap.make(["a", 1], ["b", 2]) |
| 102 | * |
| 103 | * // Increment existing value or set to 1 if not present |
| 104 | * const updateFn = (option: Option.Option<number>) => |
| 105 | * Option.isSome(option) ? Option.some(option.value + 1) : Option.some(1) |
| 106 | * |
| 107 | * const updated = HashMap.modifyAt(map, "a", updateFn) |
| 108 | * HashMap.get(updated, "a") // => Option.some(2) |
| 109 | * ``` |
| 110 | * |
| 111 | * @category models |
| 112 | * @since 2.0.0 |
| 113 | */ |
| 114 | export type UpdateFn<V> = (option: Option<V>) => Option<V> |
| 115 | |
| 116 | /** |
| 117 | * This type-level utility extracts the key type `K` from a `HashMap<K, V>` type. |
| 118 | * |
| 119 | * **Example** (Extracting key types) |
| 120 | * |
| 121 | * ```ts import.meta.vitest |
| 122 | * import { HashMap, Option } from "effect" |
| 123 | * |
| 124 | * // Create a HashMap to extract key type from |
| 125 | * const userMap = HashMap.make( |
| 126 | * ["alice", { name: "Alice", age: 30 }], |
| 127 | * ["bob", { name: "Bob", age: 25 }] |
| 128 | * ) |
| 129 | * |
| 130 | * // Extract the key type (string) |
| 131 | * type UserKey = HashMap.HashMap.Key<typeof userMap> |
| 132 | * |
| 133 | * // Use the extracted type in functions |
| 134 | * const getUserById = (id: UserKey) => HashMap.get(userMap, id) |
| 135 | * getUserById("alice") // => Option.some({ name: "Alice", age: 30 }) |
| 136 | * ``` |
| 137 | * |
| 138 | * @category utility types |
| 139 | * @since 2.0.0 |
| 140 | */ |
| 141 | export type Key<T extends HashMap<any, any>> = [T] extends [HashMap<infer _K, infer _V>] ? _K : never |
| 142 | |
| 143 | /** |
| 144 | * This type-level utility extracts the value type `V` from a `HashMap<K, V>` type. |
| 145 | * |
| 146 | * **Example** (Extracting value types) |
| 147 | * |
| 148 | * ```ts import.meta.vitest |
| 149 | * import { HashMap, Option } from "effect" |
| 150 | * |
| 151 | * // Create a HashMap with user data |
| 152 | * const userMap = HashMap.make( |
| 153 | * ["alice", { name: "Alice", age: 30, active: true }], |
| 154 | * ["bob", { name: "Bob", age: 25, active: false }] |
| 155 | * ) |
| 156 | * |
| 157 | * // Extract the value type (User object) |
| 158 | * type User = HashMap.HashMap.Value<typeof userMap> |
| 159 | * |
| 160 | * // Use the extracted type for type-safe operations |
| 161 | * const processUser = (user: User) => { |
| 162 | * return user.active ? `${user.name} (active)` : `${user.name} (inactive)` |
| 163 | * } |
| 164 | * |
| 165 | * // The lookup has type Option<User> thanks to type extraction |
| 166 | * HashMap.get(userMap, "alice") // => Option.some({ name: "Alice", age: 30, active: true }) |
| 167 | * processUser({ name: "Alice", age: 30, active: true }) // => "Alice (active)" |
| 168 | * ``` |
| 169 | * |
| 170 | * @category utility types |
| 171 | * @since 2.0.0 |
| 172 | */ |
| 173 | export type Value<T extends HashMap<any, any>> = [T] extends [HashMap<infer _K, infer _V>] ? _V : never |
| 174 | |
| 175 | /** |
| 176 | * This type-level utility extracts the entry type `[K, V]` from a `HashMap<K, V>` type. |
| 177 | * |
| 178 | * **Example** (Extracting entry types) |
| 179 | * |
| 180 | * ```ts import.meta.vitest |
| 181 | * import { HashMap } from "effect" |
| 182 | * |
| 183 | * // Create a product catalog HashMap |
| 184 | * const catalog = HashMap.make( |
| 185 | * ["laptop", { price: 999, category: "electronics" }], |
| 186 | * ["book", { price: 29, category: "education" }] |
| 187 | * ) |
| 188 | * |
| 189 | * // Extract the entry type [string, Product] |
| 190 | * type CatalogEntry = HashMap.HashMap.Entry<typeof catalog> |
| 191 | * |
| 192 | * // Use the extracted type for processing entries |
| 193 | * const processEntry = ([productId, product]: CatalogEntry) => { |
| 194 | * return `${productId}: $${product.price} (${product.category})` |
| 195 | * } |
| 196 | * |
| 197 | * // Convert to entries, process, and sort for deterministic output |
| 198 | * const descriptions = HashMap.toEntries(catalog).map(processEntry).sort() |
| 199 | * descriptions // => ["book: $29 (education)", "laptop: $999 (electronics)"] |
| 200 | * ``` |
| 201 | * |
| 202 | * @category utility types |
| 203 | * @since 3.9.0 |
| 204 | */ |
| 205 | export type Entry<T extends HashMap<any, any>> = [Key<T>, Value<T>] |
| 206 | } |
| 207 | |
| 208 | /** |
| 209 | * Checks whether a value is a HashMap. |
| 210 | * |
| 211 | * **Example** (Checking HashMap values) |
| 212 | * |
| 213 | * ```ts import.meta.vitest |
| 214 | * import { HashMap } from "effect" |
| 215 | * |
| 216 | * const map = HashMap.make(["a", 1], ["b", 2]) |
| 217 | * const notMap = { a: 1 } |
| 218 | * |
| 219 | * HashMap.isHashMap(map) // => true |
| 220 | * HashMap.isHashMap(notMap) // => false |
| 221 | * HashMap.isHashMap(null) // => false |
| 222 | * ``` |
| 223 | * |
| 224 | * @category guards |
| 225 | * @since 2.0.0 |
| 226 | */ |
| 227 | export const isHashMap: { |
| 228 | <K, V>(u: Iterable<readonly [K, V]>): u is HashMap<K, V> |
| 229 | (u: unknown): u is HashMap<unknown, unknown> |
| 230 | } = internal.isHashMap |
| 231 | |
| 232 | /** |
| 233 | * Creates a new empty `HashMap`. |
| 234 | * |
| 235 | * **Example** (Creating an empty HashMap) |
| 236 | * |
| 237 | * ```ts import.meta.vitest |
| 238 | * import { HashMap } from "effect" |
| 239 | * |
| 240 | * HashMap.empty<string, number>() // => HashMap.empty() |
| 241 | * ``` |
| 242 | * |
| 243 | * @category constructors |
| 244 | * @since 2.0.0 |
| 245 | */ |
| 246 | export const empty: <K = never, V = never>() => HashMap<K, V> = internal.empty |
| 247 | |
| 248 | /** |
| 249 | * Constructs a new `HashMap` from an array of key/value pairs. |
| 250 | * |
| 251 | * **Example** (Creating a HashMap from entries) |
| 252 | * |
| 253 | * ```ts import.meta.vitest |
| 254 | * import { HashMap } from "effect" |
| 255 | * |
| 256 | * HashMap.make(["a", 1], ["b", 2], ["c", 3]) // => HashMap.make(["a", 1], ["b", 2], ["c", 3]) |
| 257 | * ``` |
| 258 | * |
| 259 | * @category constructors |
| 260 | * @since 2.0.0 |
| 261 | */ |
| 262 | export const make: <Entries extends ReadonlyArray<readonly [any, any]>>( |
| 263 | ...entries: Entries |
| 264 | ) => HashMap< |
| 265 | Entries[number] extends readonly [infer K, any] ? K : never, |
| 266 | Entries[number] extends readonly [any, infer V] ? V : never |
| 267 | > = internal.make |
| 268 | |
| 269 | /** |
| 270 | * Creates a new `HashMap` from an iterable collection of key/value pairs. |
| 271 | * |
| 272 | * **Example** (Creating a HashMap from an iterable) |
| 273 | * |
| 274 | * ```ts import.meta.vitest |
| 275 | * import { HashMap } from "effect" |
| 276 | * |
| 277 | * const entries = [["a", 1], ["b", 2], ["c", 3]] as const |
| 278 | * HashMap.fromIterable(entries) // => HashMap.make(["a", 1], ["b", 2], ["c", 3]) |
| 279 | * ``` |
| 280 | * |
| 281 | * @category constructors |
| 282 | * @since 2.0.0 |
| 283 | */ |
| 284 | export const fromIterable: <K, V>(entries: Iterable<readonly [K, V]>) => HashMap<K, V> = internal.fromIterable |
| 285 | |
| 286 | /** |
| 287 | * Checks whether the `HashMap` contains no entries. |
| 288 | * |
| 289 | * **Example** (Checking for empty HashMaps) |
| 290 | * |
| 291 | * ```ts import.meta.vitest |
| 292 | * import { HashMap } from "effect" |
| 293 | * |
| 294 | * const emptyMap = HashMap.empty<string, number>() |
| 295 | * const nonEmptyMap = HashMap.make(["a", 1]) |
| 296 | * |
| 297 | * HashMap.isEmpty(emptyMap) // => true |
| 298 | * HashMap.isEmpty(nonEmptyMap) // => false |
| 299 | * ``` |
| 300 | * |
| 301 | * @category predicates |
| 302 | * @since 2.0.0 |
| 303 | */ |
| 304 | export const isEmpty: <K, V>(self: HashMap<K, V>) => boolean = internal.isEmpty |
| 305 | |
| 306 | /** |
| 307 | * Looks up the value for the specified key in the `HashMap` safely using the |
| 308 | * internal hashing function. |
| 309 | * |
| 310 | * **Example** (Looking up values) |
| 311 | * |
| 312 | * ```ts import.meta.vitest |
| 313 | * import { HashMap, Option } from "effect" |
| 314 | * |
| 315 | * const map = HashMap.make(["a", 1], ["b", 2]) |
| 316 | * |
| 317 | * HashMap.get(map, "a") // => Option.some(1) |
| 318 | * HashMap.get(map, "c") // => Option.none() |
| 319 | * |
| 320 | * // Using pipe syntax |
| 321 | * HashMap.get("b")(map) // => Option.some(2) |
| 322 | * ``` |
| 323 | * |
| 324 | * @category getters |
| 325 | * @since 2.0.0 |
| 326 | */ |
| 327 | export const get: { |
| 328 | <K1 extends K, K>(key: K1): <V>(self: HashMap<K, V>) => Option<V> |
| 329 | <K1 extends K, K, V>(self: HashMap<K, V>, key: K1): Option<V> |
| 330 | } = internal.get |
| 331 | |
| 332 | /** |
| 333 | * Looks up the value for the specified key in the `HashMap` safely using a custom hash. |
| 334 | * |
| 335 | * **Example** (Looking up values with a hash) |
| 336 | * |
| 337 | * ```ts import.meta.vitest |
| 338 | * import { Hash, HashMap, Option } from "effect" |
| 339 | * |
| 340 | * // Useful when implementing custom equality for complex keys |
| 341 | * const userMap = HashMap.make( |
| 342 | * ["user123", { name: "Alice", role: "admin" }], |
| 343 | * ["user456", { name: "Bob", role: "user" }] |
| 344 | * ) |
| 345 | * |
| 346 | * // Use precomputed hash for performance in hot paths |
| 347 | * const userId = "user123" |
| 348 | * const precomputedHash = Hash.string(userId) |
| 349 | * |
| 350 | * // Lookup with custom hash (e.g., cached hash value) |
| 351 | * HashMap.getHash(userMap, userId, precomputedHash) // => Option.some({ name: "Alice", role: "admin" }) |
| 352 | * |
| 353 | * // This avoids recomputing the hash when you already have it |
| 354 | * HashMap.getHash(userMap, "user999", Hash.string("user999")) // => Option.none() |
| 355 | * ``` |
| 356 | * |
| 357 | * @category getters |
| 358 | * @since 2.0.0 |
| 359 | */ |
| 360 | export const getHash: { |
| 361 | <K1 extends K, K>(key: K1, hash: number): <V>(self: HashMap<K, V>) => Option<V> |
| 362 | <K1 extends K, K, V>(self: HashMap<K, V>, key: K1, hash: number): Option<V> |
| 363 | } = internal.getHash |
| 364 | |
| 365 | /** |
| 366 | * Looks up the value for the specified key in the `HashMap` unsafely using the |
| 367 | * internal hashing function. |
| 368 | * |
| 369 | * **When to use** |
| 370 | * |
| 371 | * Use when reading from a `HashMap` by a key known to exist, and throwing is an |
| 372 | * acceptable programming error for a missing key. |
| 373 | * |
| 374 | * **Gotchas** |
| 375 | * |
| 376 | * This function throws an error if the key is not found. Use `HashMap.get` for |
| 377 | * safe access that returns `Option`. |
| 378 | * |
| 379 | * **Example** (Unsafely looking up values) |
| 380 | * |
| 381 | * ```ts import.meta.vitest |
| 382 | * import { HashMap, Option } from "effect" |
| 383 | * |
| 384 | * const config = HashMap.make( |
| 385 | * ["api_url", "https://api.example.com"], |
| 386 | * ["timeout", "5000"], |
| 387 | * ["retries", "3"] |
| 388 | * ) |
| 389 | * |
| 390 | * // Safe: use when you're certain the key exists |
| 391 | * HashMap.getUnsafe(config, "api_url") // => "https://api.example.com" |
| 392 | * |
| 393 | * // Preferred: use get() for uncertain keys |
| 394 | * HashMap.get(config, "db_url") // => Option.none() |
| 395 | * |
| 396 | * // This would throw: HashMap.getUnsafe(config, "db_url") |
| 397 | * // Error: "HashMap.getUnsafe: key not found" |
| 398 | * ``` |
| 399 | * |
| 400 | * @category unsafe |
| 401 | * @since 4.0.0 |
| 402 | */ |
| 403 | export const getUnsafe: { |
| 404 | <K1 extends K, K>(key: K1): <V>(self: HashMap<K, V>) => V |
| 405 | <K1 extends K, K, V>(self: HashMap<K, V>, key: K1): V |
| 406 | } = internal.getUnsafe |
| 407 | |
| 408 | /** |
| 409 | * Checks whether the specified key has an entry in the `HashMap`. |
| 410 | * |
| 411 | * **Example** (Checking for keys) |
| 412 | * |
| 413 | * ```ts import.meta.vitest |
| 414 | * import { HashMap } from "effect" |
| 415 | * |
| 416 | * const map = HashMap.make(["a", 1], ["b", 2]) |
| 417 | * |
| 418 | * HashMap.has(map, "a") // => true |
| 419 | * HashMap.has(map, "c") // => false |
| 420 | * |
| 421 | * // Using pipe syntax |
| 422 | * HashMap.has("b")(map) // => true |
| 423 | * ``` |
| 424 | * |
| 425 | * @category predicates |
| 426 | * @since 2.0.0 |
| 427 | */ |
| 428 | export const has: { |
| 429 | <K1 extends K, K>(key: K1): <K, V>(self: HashMap<K, V>) => boolean |
| 430 | <K1 extends K, K, V>(self: HashMap<K, V>, key: K1): boolean |
| 431 | } = internal.has |
| 432 | |
| 433 | /** |
| 434 | * Checks whether the specified key has an entry in the `HashMap` using a custom |
| 435 | * hash. |
| 436 | * |
| 437 | * **Example** (Checking keys with a hash) |
| 438 | * |
| 439 | * ```ts import.meta.vitest |
| 440 | * import { Hash, HashMap } from "effect" |
| 441 | * |
| 442 | * // Create a map with case-sensitive keys |
| 443 | * const userMap = HashMap.make( |
| 444 | * ["Admin", { role: "administrator" }], |
| 445 | * ["User", { role: "standard" }] |
| 446 | * ) |
| 447 | * |
| 448 | * // Check with exact hash |
| 449 | * const exactHash = Hash.string("Admin") |
| 450 | * HashMap.hasHash(userMap, "Admin", exactHash) // => true |
| 451 | * |
| 452 | * // A matching hash does not override key equality |
| 453 | * HashMap.hasHash(userMap, "admin", exactHash) // => false |
| 454 | * |
| 455 | * // A different hash also cannot find the existing key |
| 456 | * const lowercaseHash = Hash.string("admin") |
| 457 | * HashMap.hasHash(userMap, "Admin", lowercaseHash) // => false |
| 458 | * ``` |
| 459 | * |
| 460 | * @category predicates |
| 461 | * @since 2.0.0 |
| 462 | */ |
| 463 | export const hasHash: { |
| 464 | <K1 extends K, K>(key: K1, hash: number): <V>(self: HashMap<K, V>) => boolean |
| 465 | <K1 extends K, K, V>(self: HashMap<K, V>, key: K1, hash: number): boolean |
| 466 | } = internal.hasHash |
| 467 | |
| 468 | /** |
| 469 | * Checks whether an element matching the given predicate exists in the given `HashMap`. |
| 470 | * |
| 471 | * **Example** (Checking entries by predicate) |
| 472 | * |
| 473 | * ```ts import.meta.vitest |
| 474 | * import { HashMap } from "effect" |
| 475 | * |
| 476 | * const hm = HashMap.make([1, "a"]) |
| 477 | * HashMap.hasBy(hm, (value, key) => value === "a" && key === 1) // => true |
| 478 | * HashMap.hasBy(hm, (value) => value === "b") // => false |
| 479 | * ``` |
| 480 | * |
| 481 | * @category predicates |
| 482 | * @since 3.16.0 |
| 483 | */ |
| 484 | export const hasBy: { |
| 485 | <K, V>(predicate: (value: NoInfer<V>, key: NoInfer<K>) => boolean): (self: HashMap<K, V>) => boolean |
| 486 | <K, V>(self: HashMap<K, V>, predicate: (value: NoInfer<V>, key: NoInfer<K>) => boolean): boolean |
| 487 | } = internal.hasBy |
| 488 | |
| 489 | /** |
| 490 | * Sets the specified key to the specified value using the internal hashing |
| 491 | * function. |
| 492 | * |
| 493 | * **Example** (Setting a value) |
| 494 | * |
| 495 | * ```ts import.meta.vitest |
| 496 | * import { HashMap } from "effect" |
| 497 | * |
| 498 | * const map1 = HashMap.make(["a", 1]) |
| 499 | * HashMap.set(map1, "b", 2) // => HashMap.make(["a", 1], ["b", 2]) |
| 500 | * |
| 501 | * // Original map is unchanged |
| 502 | * map1 // => HashMap.make(["a", 1]) |
| 503 | * ``` |
| 504 | * |
| 505 | * @category transforming |
| 506 | * @since 2.0.0 |
| 507 | */ |
| 508 | export const set: { |
| 509 | <K, V>(key: K, value: V): (self: HashMap<K, V>) => HashMap<K, V> |
| 510 | <K, V>(self: HashMap<K, V>, key: K, value: V): HashMap<K, V> |
| 511 | } = internal.set |
| 512 | |
| 513 | /** |
| 514 | * Returns an `IterableIterator` of the keys within the `HashMap`. |
| 515 | * |
| 516 | * **Example** (Iterating keys) |
| 517 | * |
| 518 | * ```ts import.meta.vitest |
| 519 | * import { HashMap } from "effect" |
| 520 | * |
| 521 | * const map = HashMap.make(["a", 1], ["b", 2], ["c", 3]) |
| 522 | * Array.from(HashMap.keys(map)).sort() // => ["a", "b", "c"] |
| 523 | * ``` |
| 524 | * |
| 525 | * @category getters |
| 526 | * @since 2.0.0 |
| 527 | */ |
| 528 | export const keys: <K, V>(self: HashMap<K, V>) => IterableIterator<K> = internal.keys |
| 529 | |
| 530 | /** |
| 531 | * Returns an `IterableIterator` of the values within the `HashMap`. |
| 532 | * |
| 533 | * **Example** (Iterating values) |
| 534 | * |
| 535 | * ```ts import.meta.vitest |
| 536 | * import { HashMap } from "effect" |
| 537 | * |
| 538 | * const map = HashMap.make(["a", 1], ["b", 2], ["c", 3]) |
| 539 | * Array.from(HashMap.values(map)).sort() // => [1, 2, 3] |
| 540 | * ``` |
| 541 | * |
| 542 | * @category getters |
| 543 | * @since 2.0.0 |
| 544 | */ |
| 545 | export const values: <K, V>(self: HashMap<K, V>) => IterableIterator<V> = internal.values |
| 546 | |
| 547 | /** |
| 548 | * Returns an `Array` of the values within the `HashMap`. |
| 549 | * |
| 550 | * **Example** (Converting values to an array) |
| 551 | * |
| 552 | * ```ts import.meta.vitest |
| 553 | * import { HashMap } from "effect" |
| 554 | * |
| 555 | * const employees = HashMap.make( |
| 556 | * ["alice", { department: "engineering", salary: 90000 }], |
| 557 | * ["bob", { department: "marketing", salary: 75000 }], |
| 558 | * ["charlie", { department: "engineering", salary: 95000 }] |
| 559 | * ) |
| 560 | * |
| 561 | * // Extract all employee records |
| 562 | * const allEmployees = HashMap.toValues(employees) |
| 563 | * allEmployees.length // => 3 |
| 564 | * |
| 565 | * // Calculate total salary |
| 566 | * allEmployees.reduce((sum, emp) => sum + emp.salary, 0) // => 260000 |
| 567 | * |
| 568 | * // Filter by department |
| 569 | * allEmployees.filter((emp) => emp.department === "engineering").length // => 2 |
| 570 | * ``` |
| 571 | * |
| 572 | * @category getters |
| 573 | * @since 3.13.0 |
| 574 | */ |
| 575 | export const toValues = <K, V>(self: HashMap<K, V>): Array<V> => Array.from(values(self)) |
| 576 | |
| 577 | /** |
| 578 | * Returns an `IterableIterator` of the entries within the `HashMap`. |
| 579 | * |
| 580 | * **Example** (Iterating entries) |
| 581 | * |
| 582 | * ```ts import.meta.vitest |
| 583 | * import { HashMap } from "effect" |
| 584 | * |
| 585 | * // Create a configuration map |
| 586 | * const config = HashMap.make( |
| 587 | * ["database.host", "localhost"], |
| 588 | * ["database.port", "5432"], |
| 589 | * ["cache.enabled", "true"] |
| 590 | * ) |
| 591 | * |
| 592 | * // Sort the derived array for deterministic output |
| 593 | * const settings = Array.from(HashMap.entries(config)) |
| 594 | * .sort(([left], [right]) => left.localeCompare(right)) |
| 595 | * .map(([key, value]) => `Setting ${key} = ${value}`) |
| 596 | * |
| 597 | * settings // => ["Setting cache.enabled = true", "Setting database.host = localhost", "Setting database.port = 5432"] |
| 598 | * |
| 599 | * // Convert to array when you need all entries at once |
| 600 | * Array.from(HashMap.entries(config)).length // => 3 |
| 601 | * ``` |
| 602 | * |
| 603 | * @category getters |
| 604 | * @since 2.0.0 |
| 605 | */ |
| 606 | export const entries: <K, V>(self: HashMap<K, V>) => IterableIterator<[K, V]> = internal.entries |
| 607 | |
| 608 | /** |
| 609 | * Returns an `Array<[K, V]>` of the entries within the `HashMap`. |
| 610 | * |
| 611 | * **Example** (Converting entries to an array) |
| 612 | * |
| 613 | * ```ts import.meta.vitest |
| 614 | * import { HashMap } from "effect" |
| 615 | * |
| 616 | * const gameScores = HashMap.make( |
| 617 | * ["alice", 1250], |
| 618 | * ["bob", 980], |
| 619 | * ["charlie", 1100] |
| 620 | * ) |
| 621 | * |
| 622 | * // Convert to entries for processing |
| 623 | * const scoreEntries = HashMap.toEntries(gameScores) |
| 624 | * |
| 625 | * // Sort by score (descending) |
| 626 | * const leaderboard = scoreEntries |
| 627 | * .sort(([, a], [, b]) => b - a) |
| 628 | * .map(([player, score], rank) => `${rank + 1}. ${player}: ${score}`) |
| 629 | * |
| 630 | * leaderboard // => ["1. alice: 1250", "2. charlie: 1100", "3. bob: 980"] |
| 631 | * |
| 632 | * // Convert back to HashMap if needed |
| 633 | * HashMap.fromIterable(scoreEntries) // => HashMap.make(["alice", 1250], ["charlie", 1100], ["bob", 980]) |
| 634 | * ``` |
| 635 | * |
| 636 | * @category getters |
| 637 | * @since 2.0.0 |
| 638 | */ |
| 639 | export const toEntries = <K, V>(self: HashMap<K, V>): Array<[K, V]> => Array.from(entries(self)) |
| 640 | |
| 641 | /** |
| 642 | * Returns the number of entries within the `HashMap`. |
| 643 | * |
| 644 | * **Example** (Getting the size) |
| 645 | * |
| 646 | * ```ts import.meta.vitest |
| 647 | * import { HashMap } from "effect" |
| 648 | * |
| 649 | * const emptyMap = HashMap.empty<string, number>() |
| 650 | * const map = HashMap.make(["a", 1], ["b", 2], ["c", 3]) |
| 651 | * |
| 652 | * HashMap.size(emptyMap) // => 0 |
| 653 | * HashMap.size(map) // => 3 |
| 654 | * ``` |
| 655 | * |
| 656 | * @category getters |
| 657 | * @since 2.0.0 |
| 658 | */ |
| 659 | export const size: <K, V>(self: HashMap<K, V>) => number = internal.size |
| 660 | |
| 661 | /** |
| 662 | * Creates a transient mutable `HashMap` for efficient batched updates. |
| 663 | * |
| 664 | * **Details** |
| 665 | * |
| 666 | * Apply updates to the returned map, then call `endMutation` to finish the |
| 667 | * mutation window and use the result as an immutable `HashMap`. |
| 668 | * |
| 669 | * **Example** (Beginning batch mutation) |
| 670 | * |
| 671 | * ```ts import.meta.vitest |
| 672 | * import { HashMap } from "effect" |
| 673 | * |
| 674 | * const map = HashMap.make(["a", 1]) |
| 675 | * |
| 676 | * // Begin mutation for efficient batch operations |
| 677 | * const mutable = HashMap.beginMutation(map) |
| 678 | * |
| 679 | * // Multiple operations are now more efficient |
| 680 | * HashMap.set(mutable, "b", 2) |
| 681 | * HashMap.set(mutable, "c", 3) |
| 682 | * HashMap.remove(mutable, "a") |
| 683 | * |
| 684 | * // End mutation to get final immutable result |
| 685 | * HashMap.endMutation(mutable) // => HashMap.make(["b", 2], ["c", 3]) |
| 686 | * ``` |
| 687 | * |
| 688 | * @category mutations |
| 689 | * @since 2.0.0 |
| 690 | */ |
| 691 | export const beginMutation: <K, V>(self: HashMap<K, V>) => HashMap<K, V> = internal.beginMutation |
| 692 | |
| 693 | /** |
| 694 | * Marks the `HashMap` as immutable, completing the mutation cycle. |
| 695 | * |
| 696 | * **Example** (Ending batch mutation) |
| 697 | * |
| 698 | * ```ts import.meta.vitest |
| 699 | * import { HashMap } from "effect" |
| 700 | * |
| 701 | * // Start with an existing map |
| 702 | * const original = HashMap.make(["x", 10], ["y", 20]) |
| 703 | * |
| 704 | * // Begin mutation for batch operations |
| 705 | * const mutable = HashMap.beginMutation(original) |
| 706 | * |
| 707 | * // Perform multiple efficient operations |
| 708 | * HashMap.set(mutable, "z", 30) |
| 709 | * HashMap.remove(mutable, "x") |
| 710 | * HashMap.set(mutable, "w", 40) |
| 711 | * |
| 712 | * // End mutation to get final immutable result |
| 713 | * HashMap.endMutation(mutable) // => HashMap.make(["y", 20], ["z", 30], ["w", 40]) |
| 714 | * ``` |
| 715 | * |
| 716 | * @category mutations |
| 717 | * @since 2.0.0 |
| 718 | */ |
| 719 | export const endMutation: <K, V>(self: HashMap<K, V>) => HashMap<K, V> = internal.endMutation |
| 720 | |
| 721 | /** |
| 722 | * Runs a batch of updates against a transient mutable copy of the `HashMap` |
| 723 | * and returns the finalized immutable result. |
| 724 | * |
| 725 | * **Details** |
| 726 | * |
| 727 | * The callback may call mutation-oriented helpers such as `set` and `remove` |
| 728 | * on the transient map. |
| 729 | * |
| 730 | * **Example** (Applying batched mutations) |
| 731 | * |
| 732 | * ```ts import.meta.vitest |
| 733 | * import { HashMap } from "effect" |
| 734 | * |
| 735 | * const map1 = HashMap.make(["a", 1]) |
| 736 | * const map2 = HashMap.mutate(map1, (mutable) => { |
| 737 | * HashMap.set(mutable, "b", 2) |
| 738 | * HashMap.set(mutable, "c", 3) |
| 739 | * }) |
| 740 | * map2 // => HashMap.make(["a", 1], ["b", 2], ["c", 3]) |
| 741 | * ``` |
| 742 | * |
| 743 | * @category mutations |
| 744 | * @since 2.0.0 |
| 745 | */ |
| 746 | export const mutate: { |
| 747 | <K, V>(f: (self: HashMap<K, V>) => void): (self: HashMap<K, V>) => HashMap<K, V> |
| 748 | <K, V>(self: HashMap<K, V>, f: (self: HashMap<K, V>) => void): HashMap<K, V> |
| 749 | } = internal.mutate |
| 750 | |
| 751 | /** |
| 752 | * Sets or removes the specified key using an update function. |
| 753 | * |
| 754 | * **Details** |
| 755 | * |
| 756 | * The update function receives `Some(value)` when the key exists or `None` |
| 757 | * when it does not. Returning `Some(newValue)` stores the value, and returning |
| 758 | * `None` removes the key or leaves it absent. |
| 759 | * |
| 760 | * **Example** (Updating values with Options) |
| 761 | * |
| 762 | * ```ts import.meta.vitest |
| 763 | * import { HashMap, Option } from "effect" |
| 764 | * |
| 765 | * const map = HashMap.make(["a", 1], ["b", 2]) |
| 766 | * |
| 767 | * // Increment existing value or set to 1 if not present |
| 768 | * const updateFn = (option: Option.Option<number>) => |
| 769 | * Option.isSome(option) ? Option.some(option.value + 1) : Option.some(1) |
| 770 | * |
| 771 | * const updated = HashMap.modifyAt(map, "a", updateFn) |
| 772 | * HashMap.get(updated, "a") // => Option.some(2) |
| 773 | * ``` |
| 774 | * |
| 775 | * @category transforming |
| 776 | * @since 2.0.0 |
| 777 | */ |
| 778 | export const modifyAt: { |
| 779 | <K, V>(key: K, f: HashMap.UpdateFn<V>): (self: HashMap<K, V>) => HashMap<K, V> |
| 780 | <K, V>(self: HashMap<K, V>, key: K, f: HashMap.UpdateFn<V>): HashMap<K, V> |
| 781 | } = internal.modifyAt |
| 782 | |
| 783 | /** |
| 784 | * Sets or removes the specified key using a precomputed hash and an update |
| 785 | * function. |
| 786 | * |
| 787 | * **Details** |
| 788 | * |
| 789 | * The update function receives `Some(value)` when the key exists or `None` |
| 790 | * when it does not. Returning `Some(newValue)` stores the value, and returning |
| 791 | * `None` removes the key or leaves it absent. |
| 792 | * |
| 793 | * **Example** (Updating values with a hash) |
| 794 | * |
| 795 | * ```ts import.meta.vitest |
| 796 | * import { Hash, HashMap, Option } from "effect" |
| 797 | * |
| 798 | * // Useful when working with precomputed hashes for performance |
| 799 | * const counters = HashMap.make(["downloads", 100], ["views", 250]) |
| 800 | * |
| 801 | * // Cache hash computation for frequently accessed keys |
| 802 | * const metricKey = "downloads" |
| 803 | * const cachedHash = Hash.string(metricKey) |
| 804 | * |
| 805 | * // Update function that increments counter or initializes to 1 |
| 806 | * const incrementCounter = (current: Option.Option<number>) => |
| 807 | * Option.isSome(current) ? Option.some(current.value + 1) : Option.some(1) |
| 808 | * |
| 809 | * // Use cached hash for efficient updates in loops |
| 810 | * const updated = HashMap.modifyHash( |
| 811 | * counters, |
| 812 | * metricKey, |
| 813 | * cachedHash, |
| 814 | * incrementCounter |
| 815 | * ) |
| 816 | * HashMap.get(updated, "downloads") // => Option.some(101) |
| 817 | * |
| 818 | * // Add new metric with precomputed hash |
| 819 | * const newMetric = "clicks" |
| 820 | * const clicksHash = Hash.string(newMetric) |
| 821 | * const withClicks = HashMap.modifyHash( |
| 822 | * updated, |
| 823 | * newMetric, |
| 824 | * clicksHash, |
| 825 | * incrementCounter |
| 826 | * ) |
| 827 | * HashMap.get(withClicks, "clicks") // => Option.some(1) |
| 828 | * ``` |
| 829 | * |
| 830 | * @category transforming |
| 831 | * @since 2.0.0 |
| 832 | */ |
| 833 | export const modifyHash: { |
| 834 | <K, V>(key: K, hash: number, f: HashMap.UpdateFn<V>): (self: HashMap<K, V>) => HashMap<K, V> |
| 835 | <K, V>(self: HashMap<K, V>, key: K, hash: number, f: HashMap.UpdateFn<V>): HashMap<K, V> |
| 836 | } = internal.modifyHash |
| 837 | |
| 838 | /** |
| 839 | * Updates the value of the specified key within the `HashMap` if it exists. |
| 840 | * |
| 841 | * **Example** (Modifying existing values) |
| 842 | * |
| 843 | * ```ts import.meta.vitest |
| 844 | * import { HashMap, Option } from "effect" |
| 845 | * |
| 846 | * const map1 = HashMap.make(["a", 1], ["b", 2]) |
| 847 | * const map2 = HashMap.modify(map1, "a", (value) => value * 3) |
| 848 | * |
| 849 | * HashMap.get(map2, "a") // => Option.some(3) |
| 850 | * HashMap.get(map2, "b") // => Option.some(2) |
| 851 | * ``` |
| 852 | * |
| 853 | * @category transforming |
| 854 | * @since 2.0.0 |
| 855 | */ |
| 856 | export const modify: { |
| 857 | <K, V>(key: K, f: (v: V) => V): (self: HashMap<K, V>) => HashMap<K, V> |
| 858 | <K, V>(self: HashMap<K, V>, key: K, f: (v: V) => V): HashMap<K, V> |
| 859 | } = internal.modify |
| 860 | |
| 861 | /** |
| 862 | * Combines two `HashMap`s into one. |
| 863 | * |
| 864 | * **Details** |
| 865 | * |
| 866 | * Entries from `that` are inserted into `self`; when both maps contain an |
| 867 | * equal key, the value from `that` replaces the value from `self`. |
| 868 | * |
| 869 | * **Example** (Combining HashMaps) |
| 870 | * |
| 871 | * ```ts import.meta.vitest |
| 872 | * import { HashMap, Option } from "effect" |
| 873 | * |
| 874 | * const map1 = HashMap.make(["a", 1], ["b", 2]) |
| 875 | * const map2 = HashMap.make(["b", 20], ["c", 3]) |
| 876 | * const union = HashMap.union(map1, map2) |
| 877 | * |
| 878 | * union // => HashMap.make(["a", 1], ["b", 20], ["c", 3]) |
| 879 | * HashMap.get(union, "b") // => Option.some(20) |
| 880 | * ``` |
| 881 | * |
| 882 | * @category combining |
| 883 | * @since 2.0.0 |
| 884 | */ |
| 885 | export const union: { |
| 886 | <K1, V1>(that: HashMap<K1, V1>): <K0, V0>(self: HashMap<K0, V0>) => HashMap<K1 | K0, V1 | V0> |
| 887 | <K0, V0, K1, V1>(self: HashMap<K0, V0>, that: HashMap<K1, V1>): HashMap<K0 | K1, V0 | V1> |
| 888 | } = internal.union |
| 889 | |
| 890 | /** |
| 891 | * Removes the entry for the specified key in the `HashMap` using the internal |
| 892 | * hashing function. |
| 893 | * |
| 894 | * **Example** (Removing a key) |
| 895 | * |
| 896 | * ```ts import.meta.vitest |
| 897 | * import { HashMap } from "effect" |
| 898 | * |
| 899 | * const map1 = HashMap.make(["a", 1], ["b", 2], ["c", 3]) |
| 900 | * const map2 = HashMap.remove(map1, "b") |
| 901 | * |
| 902 | * map2 // => HashMap.make(["a", 1], ["c", 3]) |
| 903 | * ``` |
| 904 | * |
| 905 | * @category transforming |
| 906 | * @since 2.0.0 |
| 907 | */ |
| 908 | export const remove: { |
| 909 | <K>(key: K): <V>(self: HashMap<K, V>) => HashMap<K, V> |
| 910 | <K, V>(self: HashMap<K, V>, key: K): HashMap<K, V> |
| 911 | } = internal.remove |
| 912 | |
| 913 | /** |
| 914 | * Removes all entries in the `HashMap` which have the specified keys. |
| 915 | * |
| 916 | * **Example** (Removing multiple keys) |
| 917 | * |
| 918 | * ```ts import.meta.vitest |
| 919 | * import { HashMap } from "effect" |
| 920 | * |
| 921 | * const map1 = HashMap.make(["a", 1], ["b", 2], ["c", 3], ["d", 4]) |
| 922 | * const map2 = HashMap.removeMany(map1, ["b", "d"]) |
| 923 | * |
| 924 | * map2 // => HashMap.make(["a", 1], ["c", 3]) |
| 925 | * ``` |
| 926 | * |
| 927 | * @category transforming |
| 928 | * @since 2.0.0 |
| 929 | */ |
| 930 | export const removeMany: { |
| 931 | <K>(keys: Iterable<K>): <V>(self: HashMap<K, V>) => HashMap<K, V> |
| 932 | <K, V>(self: HashMap<K, V>, keys: Iterable<K>): HashMap<K, V> |
| 933 | } = internal.removeMany |
| 934 | |
| 935 | /** |
| 936 | * Sets multiple key-value pairs in the `HashMap`. |
| 937 | * |
| 938 | * **Example** (Setting multiple entries) |
| 939 | * |
| 940 | * ```ts import.meta.vitest |
| 941 | * import { HashMap, Option } from "effect" |
| 942 | * |
| 943 | * const map1 = HashMap.make(["a", 1], ["b", 2]) |
| 944 | * const newEntries = [["c", 3], ["d", 4], ["a", 10]] as const // "a" will be overwritten |
| 945 | * const map2 = HashMap.setMany(map1, newEntries) |
| 946 | * |
| 947 | * map2 // => HashMap.make(["a", 10], ["b", 2], ["c", 3], ["d", 4]) |
| 948 | * HashMap.get(map2, "a") // => Option.some(10) |
| 949 | * ``` |
| 950 | * |
| 951 | * @category transforming |
| 952 | * @since 4.0.0 |
| 953 | */ |
| 954 | export const setMany: { |
| 955 | <K, V>(entries: Iterable<readonly [K, V]>): (self: HashMap<K, V>) => HashMap<K, V> |
| 956 | <K, V>(self: HashMap<K, V>, entries: Iterable<readonly [K, V]>): HashMap<K, V> |
| 957 | } = internal.setMany |
| 958 | |
| 959 | /** |
| 960 | * Maps over the entries of the `HashMap` using the specified function. |
| 961 | * |
| 962 | * **Example** (Mapping values) |
| 963 | * |
| 964 | * ```ts import.meta.vitest |
| 965 | * import { HashMap, Option } from "effect" |
| 966 | * |
| 967 | * const map1 = HashMap.make(["a", 1], ["b", 2], ["c", 3]) |
| 968 | * const map2 = HashMap.map(map1, (value, key) => `${key}:${value * 2}`) |
| 969 | * |
| 970 | * HashMap.get(map2, "a") // => Option.some("a:2") |
| 971 | * HashMap.get(map2, "b") // => Option.some("b:4") |
| 972 | * ``` |
| 973 | * |
| 974 | * @category mapping |
| 975 | * @since 2.0.0 |
| 976 | */ |
| 977 | export const map: { |
| 978 | <A, V, K>(f: (value: V, key: K) => A): (self: HashMap<K, V>) => HashMap<K, A> |
| 979 | <K, V, A>(self: HashMap<K, V>, f: (value: V, key: K) => A): HashMap<K, A> |
| 980 | } = internal.map |
| 981 | |
| 982 | /** |
| 983 | * Maps each entry to a `HashMap` and flattens the results. |
| 984 | * |
| 985 | * **Gotchas** |
| 986 | * |
| 987 | * The hash and equality behavior of both maps have to be the same. |
| 988 | * |
| 989 | * **Example** (Flat mapping values) |
| 990 | * |
| 991 | * ```ts import.meta.vitest |
| 992 | * import { HashMap, Option } from "effect" |
| 993 | * |
| 994 | * const map1 = HashMap.make(["a", 1], ["b", 2]) |
| 995 | * const map2 = HashMap.flatMap( |
| 996 | * map1, |
| 997 | * (value, key) => HashMap.make([key + "1", value], [key + "2", value * 2]) |
| 998 | * ) |
| 999 | * |
| 1000 | * map2 // => HashMap.make(["a1", 1], ["a2", 2], ["b1", 2], ["b2", 4]) |
| 1001 | * HashMap.get(map2, "b2") // => Option.some(4) |
| 1002 | * ``` |
| 1003 | * |
| 1004 | * @category sequencing |
| 1005 | * @since 2.0.0 |
| 1006 | */ |
| 1007 | export const flatMap: { |
| 1008 | <A, K, B>(f: (value: A, key: K) => HashMap<K, B>): (self: HashMap<K, A>) => HashMap<K, B> |
| 1009 | <K, A, B>(self: HashMap<K, A>, f: (value: A, key: K) => HashMap<K, B>): HashMap<K, B> |
| 1010 | } = internal.flatMap |
| 1011 | |
| 1012 | /** |
| 1013 | * Applies the specified function to the entries of the `HashMap`. |
| 1014 | * |
| 1015 | * **Example** (Iterating with side effects) |
| 1016 | * |
| 1017 | * ```ts import.meta.vitest |
| 1018 | * import { HashMap } from "effect" |
| 1019 | * |
| 1020 | * const map = HashMap.make(["a", 1], ["b", 2]) |
| 1021 | * const collected: Array<[string, number]> = [] |
| 1022 | * |
| 1023 | * HashMap.forEach(map, (value, key) => { |
| 1024 | * collected.push([key, value]) |
| 1025 | * }) |
| 1026 | * |
| 1027 | * collected.sort() // => [["a", 1], ["b", 2]] |
| 1028 | * ``` |
| 1029 | * |
| 1030 | * @category traversing |
| 1031 | * @since 2.0.0 |
| 1032 | */ |
| 1033 | export const forEach: { |
| 1034 | <V, K>(f: (value: V, key: K) => void): (self: HashMap<K, V>) => void |
| 1035 | <V, K>(self: HashMap<K, V>, f: (value: V, key: K) => void): void |
| 1036 | } = internal.forEach |
| 1037 | |
| 1038 | /** |
| 1039 | * Reduces the specified state over the entries of the `HashMap`. |
| 1040 | * |
| 1041 | * **Example** (Reducing values) |
| 1042 | * |
| 1043 | * ```ts import.meta.vitest |
| 1044 | * import { HashMap } from "effect" |
| 1045 | * |
| 1046 | * const map = HashMap.make(["a", 1], ["b", 2], ["c", 3]) |
| 1047 | * HashMap.reduce(map, 0, (acc, value) => acc + value) // => 6 |
| 1048 | * ``` |
| 1049 | * |
| 1050 | * @category folding |
| 1051 | * @since 2.0.0 |
| 1052 | */ |
| 1053 | export const reduce: { |
| 1054 | <Z, V, K>(zero: Z, f: (accumulator: Z, value: V, key: K) => Z): (self: HashMap<K, V>) => Z |
| 1055 | <K, V, Z>(self: HashMap<K, V>, zero: Z, f: (accumulator: Z, value: V, key: K) => Z): Z |
| 1056 | } = internal.reduce |
| 1057 | |
| 1058 | /** |
| 1059 | * Filters entries out of a `HashMap` using the specified predicate. |
| 1060 | * |
| 1061 | * **Example** (Filtering entries) |
| 1062 | * |
| 1063 | * ```ts import.meta.vitest |
| 1064 | * import { HashMap } from "effect" |
| 1065 | * |
| 1066 | * const map1 = HashMap.make(["a", 1], ["b", 2], ["c", 3], ["d", 4]) |
| 1067 | * const map2 = HashMap.filter(map1, (value) => value % 2 === 0) |
| 1068 | * |
| 1069 | * map2 // => HashMap.make(["b", 2], ["d", 4]) |
| 1070 | * ``` |
| 1071 | * |
| 1072 | * @category filtering |
| 1073 | * @since 2.0.0 |
| 1074 | */ |
| 1075 | export const filter: { |
| 1076 | <K, A>(f: (a: NoInfer<A>, k: K) => boolean): (self: HashMap<K, A>) => HashMap<K, A> |
| 1077 | <K, A>(self: HashMap<K, A>, f: (a: A, k: K) => boolean): HashMap<K, A> |
| 1078 | } = internal.filter |
| 1079 | |
| 1080 | /** |
| 1081 | * Filters out `None` values from a `HashMap` of `Options`s. |
| 1082 | * |
| 1083 | * **Example** (Compacting Option values) |
| 1084 | * |
| 1085 | * ```ts import.meta.vitest |
| 1086 | * import { HashMap, Option } from "effect" |
| 1087 | * |
| 1088 | * const map1 = HashMap.make( |
| 1089 | * ["a", Option.some(1)], |
| 1090 | * ["b", Option.none()], |
| 1091 | * ["c", Option.some(3)] |
| 1092 | * ) |
| 1093 | * const map2 = HashMap.compact(map1) |
| 1094 | * |
| 1095 | * map2 // => HashMap.make(["a", 1], ["c", 3]) |
| 1096 | * HashMap.get(map2, "a") // => Option.some(1) |
| 1097 | * ``` |
| 1098 | * |
| 1099 | * @category filtering |
| 1100 | * @since 2.0.0 |
| 1101 | */ |
| 1102 | export const compact: <K, A>(self: HashMap<K, Option<A>>) => HashMap<K, A> = internal.compact |
| 1103 | |
| 1104 | /** |
| 1105 | * Maps over the entries of the `HashMap` using the specified filter and keeps |
| 1106 | * only successful results. |
| 1107 | * |
| 1108 | * **Example** (Filtering and mapping Results) |
| 1109 | * |
| 1110 | * ```ts import.meta.vitest |
| 1111 | * import { HashMap, Option, Result } from "effect" |
| 1112 | * |
| 1113 | * const map1 = HashMap.make(["a", 1], ["b", 2], ["c", 3], ["d", 4]) |
| 1114 | * const map2 = HashMap.filterMap( |
| 1115 | * map1, |
| 1116 | * (value) => value % 2 === 0 ? Result.succeed(value * 2) : Result.failVoid |
| 1117 | * ) |
| 1118 | * |
| 1119 | * map2 // => HashMap.make(["b", 4], ["d", 8]) |
| 1120 | * HashMap.get(map2, "b") // => Option.some(4) |
| 1121 | * ``` |
| 1122 | * |
| 1123 | * @category filtering |
| 1124 | * @since 2.0.0 |
| 1125 | */ |
| 1126 | export const filterMap: { |
| 1127 | <A, K, B, X>(f: (input: A, key: K) => Result<B, X>): (self: HashMap<K, A>) => HashMap<K, B> |
| 1128 | <K, A, B, X>(self: HashMap<K, A>, f: (input: A, key: K) => Result<B, X>): HashMap<K, B> |
| 1129 | } = internal.filterMap |
| 1130 | |
| 1131 | /** |
| 1132 | * Returns the first element that satisfies the specified |
| 1133 | * predicate, or `None` if no such element exists. |
| 1134 | * |
| 1135 | * **Example** (Finding the first matching entry) |
| 1136 | * |
| 1137 | * ```ts import.meta.vitest |
| 1138 | * import { HashMap, Option } from "effect" |
| 1139 | * |
| 1140 | * const map = HashMap.make(["a", 1], ["b", 2], ["c", 3]) |
| 1141 | * HashMap.findFirst(map, (value, key) => key === "b" && value > 1) // => Option.some(["b", 2]) |
| 1142 | * ``` |
| 1143 | * |
| 1144 | * @category searching |
| 1145 | * @since 2.0.0 |
| 1146 | */ |
| 1147 | export const findFirst: { |
| 1148 | <K, A>(predicate: (a: NoInfer<A>, k: K) => boolean): (self: HashMap<K, A>) => Option<[K, A]> |
| 1149 | <K, A>(self: HashMap<K, A>, predicate: (a: A, k: K) => boolean): Option<[K, A]> |
| 1150 | } = internal.findFirst |
| 1151 | |
| 1152 | /** |
| 1153 | * Checks whether any entry in a hashmap meets a specific condition. |
| 1154 | * |
| 1155 | * **Example** (Checking for any matching entry) |
| 1156 | * |
| 1157 | * ```ts import.meta.vitest |
| 1158 | * import { HashMap } from "effect" |
| 1159 | * |
| 1160 | * const map = HashMap.make(["a", 1], ["b", 2], ["c", 3]) |
| 1161 | * |
| 1162 | * HashMap.some(map, (value) => value > 2) // => true |
| 1163 | * HashMap.some(map, (value) => value > 5) // => false |
| 1164 | * ``` |
| 1165 | * |
| 1166 | * @category predicates |
| 1167 | * @since 3.13.0 |
| 1168 | */ |
| 1169 | export const some: { |
| 1170 | <K, A>(predicate: (a: NoInfer<A>, k: K) => boolean): (self: HashMap<K, A>) => boolean |
| 1171 | <K, A>(self: HashMap<K, A>, predicate: (a: A, k: K) => boolean): boolean |
| 1172 | } = internal.some |
| 1173 | |
| 1174 | /** |
| 1175 | * Checks whether all entries in a hashmap meets a specific condition. |
| 1176 | * |
| 1177 | * **Example** (Checking all entries) |
| 1178 | * |
| 1179 | * ```ts import.meta.vitest |
| 1180 | * import { HashMap } from "effect" |
| 1181 | * |
| 1182 | * const map = HashMap.make(["a", 1], ["b", 2], ["c", 3]) |
| 1183 | * |
| 1184 | * HashMap.every(map, (value) => value > 0) // => true |
| 1185 | * HashMap.every(map, (value) => value > 1) // => false |
| 1186 | * ``` |
| 1187 | * |
| 1188 | * @category predicates |
| 1189 | * @since 3.14.0 |
| 1190 | */ |
| 1191 | export const every: { |
| 1192 | <K, A>(predicate: (a: NoInfer<A>, k: K) => boolean): (self: HashMap<K, A>) => boolean |
| 1193 | <K, A>(self: HashMap<K, A>, predicate: (a: A, k: K) => boolean): boolean |
| 1194 | } = internal.every |