Skip to content

Commit

Permalink
add get_or_init for hashmap
Browse files Browse the repository at this point in the history
  • Loading branch information
hackwaly committed Dec 20, 2024
1 parent 7b8f620 commit d0e1c76
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 0 deletions.
39 changes: 39 additions & 0 deletions hashmap/hashmap.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,45 @@ pub fn op_get[K : Hash + Eq, V](self : T[K, V], key : K) -> V? {
self.get(key)
}

///|
/// Gets the value associated with the given key. If the key doesn't exist in the
/// map, initializes it with the given function and returns the new value.
///
/// Parameters:
///
/// * `map` : The hash map to operate on.
/// * `key` : The key to look up in the map.
/// * `init_fn` : A function that takes the key and returns a new value. This
/// function is called only when the key is not found in the map.
///
/// Returns the value associated with the key, either existing or newly
/// initialized.
///
/// Example:
///
/// ```moonbit
/// test "get_or_init" {
/// let map : @hashmap.T[String, Int] = @hashmap.new()
/// let v = map.get_or_init("key", fn(k) { k.length() })
/// inspect!(v, content="3")
/// inspect!(map.get("key"), content="Some(3)")
/// }
/// ```
pub fn get_or_init[K : Hash + Eq, V](
self : T[K, V],
key : K,
init : (K) -> V
) -> V {
match self.get(key) {
Some(v) => v
None => {
let v = init(key)
self.set(key, v)
v
}
}
}

///|
/// Get the value associated with a key,
/// returns the provided default value if the key does not exist.
Expand Down
1 change: 1 addition & 0 deletions hashmap/hashmap.mbti
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ impl T {
from_iter[K : Hash + Eq, V](Iter[(K, V)]) -> Self[K, V]
get[K : Hash + Eq, V](Self[K, V], K) -> V?
get_or_default[K : Hash + Eq, V](Self[K, V], K, V) -> V
get_or_init[K : Hash + Eq, V](Self[K, V], K, (K) -> V) -> V
is_empty[K, V](Self[K, V]) -> Bool
iter[K, V](Self[K, V]) -> Iter[(K, V)]
iter2[K, V](Self[K, V]) -> Iter2[K, V]
Expand Down
34 changes: 34 additions & 0 deletions hashmap/hashmap_test.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -289,3 +289,37 @@ test "from_iter empty iter" {
let map : @hashmap.T[Int, Int] = @hashmap.T::from_iter(Iter::empty())
inspect!(map, content="HashMap::of([])")
}

test "@hashmap.T::get_or_init/init_new_key" {
let map : @hashmap.T[String, Int] = @hashmap.T::new()
// Test initialization of a new key
let v1 = map.get_or_init("test", fn(__) { __.length() })
inspect!(v1, content="4")
inspect!(map.get("test"), content="Some(4)")
}

test "@hashmap.T::get_or_init/existing_key" {
let map : @hashmap.T[String, Int] = @hashmap.T::new()
map.set("key", 42)
// Test retrieval of existing key without calling init function
let v2 = map.get_or_init("key", fn(__) { __.length() })
inspect!(v2, content="42")
inspect!(map.get("key"), content="Some(42)")
}

test "@hashmap.T::get_or_init/multiple_calls" {
let map : @hashmap.T[String, Int] = @hashmap.T::new()
let mut counter = 0
// Test that init function is only called once for the same key
let v1 = map.get_or_init("test", fn(__) {
counter = counter + 1
counter
})
let v2 = map.get_or_init("test", fn(__) {
counter = counter + 1
counter
})
inspect!(v1, content="1")
inspect!(v2, content="1")
inspect!(counter, content="1")
}

0 comments on commit d0e1c76

Please sign in to comment.