@effect-ts/system
Version:
Effect-TS is a zero dependency set of libraries to write highly productive, purely functional TypeScript at scale.
280 lines (239 loc) • 6.18 kB
text/typescript
// ets_tracing: off
import * as I from "../../../Iterable/index.js"
import * as O from "../../../Option/index.js"
import * as St from "../../../Structural/index.js"
import { AtomicNumber } from "../../../Support/AtomicNumber/index.js"
export const HashMapTypeId = Symbol()
export type HashMapTypeId = typeof HashMapTypeId
class Node<K, V> implements Iterable<readonly [K, V]> {
constructor(readonly k: K, public v: V, public next?: Node<K, V>) {}
[Symbol.iterator](): Iterator<readonly [K, V]> {
// eslint-disable-next-line @typescript-eslint/no-this-alias
let c: Node<K, V> | undefined = this
let n = 0
return {
next: () => {
if (c) {
const kv = [c.k, c.v] as const
c = c.next
n++
return {
value: kv,
done: false
}
} else {
return {
value: n,
done: true
}
}
}
}
}
}
/**
* A Mutable HashMap
*/
export class HashMap<K, V> implements Iterable<readonly [K, V]> {
readonly _typeId: HashMapTypeId = HashMapTypeId
readonly backingMap = new Map<number, Node<K, V>>()
readonly length = new AtomicNumber(0)
get(k: K): O.Option<V> {
const hash = St.hash(k)
const arr = this.backingMap.get(hash)
if (typeof arr === "undefined") {
return O.none
}
let c: Node<K, V> | undefined = arr
while (c) {
if (St.equals(k, c.k)) {
return O.some(c.v)
}
c = c.next
}
return O.none
}
remove(k: K): HashMap<K, V> {
const hash = St.hash(k)
const arr = this.backingMap.get(hash)
if (typeof arr === "undefined") {
return this
}
if (St.equals(k, arr.k)) {
if (typeof arr.next !== "undefined") {
this.backingMap.set(hash, arr.next)
} else {
this.backingMap.delete(hash)
}
this.length.decrementAndGet()
return this
}
let next: Node<K, V> | undefined = arr.next
let curr = arr
while (next) {
if (St.equals(k, next.k)) {
curr.next = next.next
this.length.decrementAndGet()
return this
}
curr = next
next = next.next
}
return this
}
set(k: K, v: V): HashMap<K, V> {
const hash = St.hash(k)
const arr = this.backingMap.get(hash)
if (typeof arr === "undefined") {
this.backingMap.set(hash, new Node(k, v))
this.length.incrementAndGet()
return this
}
let c: Node<K, V> | undefined = arr
let l = arr
while (c) {
if (St.equals(k, c.k)) {
c.v = v
return this
}
l = c
c = c.next
}
this.length.incrementAndGet()
l.next = new Node(k, v)
return this
}
update(k: K, f: (v: V) => V): HashMap<K, V> {
const hash = St.hash(k)
const arr = this.backingMap.get(hash)
if (typeof arr === "undefined") {
return this
}
let c: Node<K, V> | undefined = arr
while (c) {
if (St.equals(k, c.k)) {
c.v = f(c.v)
return this
}
c = c.next
}
return this
}
[Symbol.iterator](): Iterator<readonly [K, V]> {
return I.chain_(this.backingMap, ([, _]) => _)[Symbol.iterator]()
}
}
/**
* Creates a new map
*/
export function make<K, V>() {
return new HashMap<K, V>()
}
/**
* Creates a new map from an Iterable
*/
export function from<K, V>(xs: Iterable<readonly [K, V]>): HashMap<K, V> {
const res = make<K, V>()
for (const p of xs) {
res.set(...p)
}
return res
}
/**
* Lookup the value for `key` in `map` using internal hash function.
*/
export function get_<K, V>(map: HashMap<K, V>, key: K): O.Option<V> {
return map.get(key)
}
/**
* Lookup the value for `key` in `map` using internal hash function.
*
* @ets_data_first get_
*/
export function get<K>(key: K) {
return <V>(map: HashMap<K, V>) => get_(map, key)
}
/**
* Store `value` for `key` in `map` using internal hash function.
*/
export function set_<K, V>(map: HashMap<K, V>, key: K, value: V) {
return map.set(key, value)
}
/**
* Store `value` for `key` in `map` using internal hash function.
*
* @ets_data_first set_
*/
export function set<K, V>(key: K, value: V) {
return (map: HashMap<K, V>) => set_(map, key, value)
}
/**
* Remove the entry for `key` in `map` using internal hash.
*/
export function remove_<K, V>(map: HashMap<K, V>, key: K): HashMap<K, V> {
return map.remove(key)
}
/**
* Remove the entry for `key` in `map` using internal hash.
*
* @ets_data_first remove_
*/
export function remove<K>(key: K) {
return <V>(map: HashMap<K, V>) => remove_(map, key)
}
/**
* Calculate the number of key/value pairs in a map
*/
export function size<K, V>(map: HashMap<K, V>) {
return map.length.get
}
/**
* Update a value if exists
*/
export function update_<K, V>(map: HashMap<K, V>, key: K, f: (v: V) => V) {
return map.update(key, f)
}
/**
* Update a value if exists
*
* @ets_data_first update_
*/
export function update<K, V>(key: K, f: (v: V) => V) {
return (map: HashMap<K, V>) => update_(map, key, f)
}
/**
* Alter the value stored for `key` in `map` using function `f` using internal hash function.
*
* `f` is invoked with the current value for `k` if it exists,
* or no arguments if no such value exists.
*
* `modify` will always either update or insert a value into the map.
* Returns a map with the modified value. Does not alter `map`.
*/
export function modify_<K, V>(
map: HashMap<K, V>,
key: K,
f: (v: O.Option<V>) => O.Option<V>
) {
const v = f(map.get(key))
if (O.isSome(v)) {
map.set(key, v.value)
} else {
map.remove(key)
}
return map
}
/**
* Alter the value stored for `key` in `map` using function `f` using internal hash function.
*
* `f` is invoked with the current value for `k` if it exists,
* or no arguments if no such value exists.
*
* `modify` will always either update or insert a value into the map.
* Returns a map with the modified value. Does not alter `map`.
*
* @ets_data_first modify_
*/
export function modify<K, V>(key: K, f: (v: O.Option<V>) => O.Option<V>) {
return (map: HashMap<K, V>) => modify_(map, key, f)
}