diff options
author | Giacomo Cavalieri <giacomo.cavalieri@icloud.com> | 2024-05-21 09:45:35 +0200 |
---|---|---|
committer | Louis Pilfold <louis@lpil.uk> | 2024-05-24 16:10:09 +0100 |
commit | f66ad5672d061bbaba0af9458da6f1f4fe0211d9 (patch) | |
tree | 683edf993cd96254502b14972602f6498405893f | |
parent | 84f573260eddae1e581dd7894915cb3181440b02 (diff) | |
download | gleam_stdlib-f66ad5672d061bbaba0af9458da6f1f4fe0211d9.tar.gz gleam_stdlib-f66ad5672d061bbaba0af9458da6f1f4fe0211d9.zip |
The `dict` module gains the `combine` function
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | src/gleam/dict.gleam | 48 | ||||
-rw-r--r-- | test/gleam/dict_test.gleam | 26 |
3 files changed, 64 insertions, 11 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f7b3cb..42c5a4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ correctly on Erlang. - `dynamic.unsafe_coerce` function has been deprecated. - Fixed `bit_array` slices of slices sometimes being incorrect on JavaScript. +- The `dict` module gains the `combine` function. ## v0.37.0 - 2024-04-19 diff --git a/src/gleam/dict.gleam b/src/gleam/dict.gleam index 6013f32..f44daa3 100644 --- a/src/gleam/dict.gleam +++ b/src/gleam/dict.gleam @@ -43,13 +43,13 @@ pub fn size(dict: Dict(k, v)) -> Int /// ## Examples /// /// Calling `to_list` on an empty `dict` returns an empty list. -/// +/// /// ```gleam /// new() |> to_list /// // -> [] /// ``` -/// -/// The ordering of elements in the resulting list is an implementation detail +/// +/// The ordering of elements in the resulting list is an implementation detail /// that should not be relied upon. /// /// ```gleam @@ -498,17 +498,17 @@ pub fn fold( |> do_fold(initial, fun) } -/// Calls a function for each key and value in a dict, discarding the return +/// Calls a function for each key and value in a dict, discarding the return /// value. -/// +/// /// Useful for producing a side effect for every item of a dict. -/// +/// /// ```gleam /// import gleam/io -/// +/// /// let dict = from_list([#("a", "apple"), #("b", "banana"), #("c", "cherry")]) -/// -/// each(dict, fn(key, value) { +/// +/// each(dict, fn(key, value) { /// io.println(key <> " => " <> value) /// }) /// // -> Nil @@ -516,13 +516,39 @@ pub fn fold( /// // b => banana /// // c => cherry /// ``` -/// +/// /// The order of elements in the iteration is an implementation detail that /// should not be relied upon. -/// +/// pub fn each(dict: Dict(k, v), fun: fn(k, v) -> b) -> Nil { fold(dict, Nil, fn(nil, k, v) { fun(k, v) nil }) } + +/// Creates a new dict from a pair of given dicts by combining their entries. +/// +/// If there are entries with the same keys in both dicts the given function is +/// used to determine the new value to use in the resulting dict. +/// +/// ## Examples +/// +/// ```gleam +/// let a = from_list([#("a", 0), #("b", 1)]) +/// let b = from_list([#("a", 2), #("c", 3)]) +/// combine(a, b, fn(one, other) { one + other }) +/// // -> from_list([#("a", 2), #("b", 1), #("c", 3)]) +/// ``` +/// +pub fn combine( + dict: Dict(k, v), + other: Dict(k, v), + with fun: fn(v, v) -> v, +) -> Dict(k, v) { + use acc, key, value <- fold(over: dict, from: other) + case get(acc, key) { + Ok(other_value) -> insert(acc, key, fun(value, other_value)) + Error(_) -> insert(acc, key, value) + } +} diff --git a/test/gleam/dict_test.gleam b/test/gleam/dict_test.gleam index ca77bb9..0bcb334 100644 --- a/test/gleam/dict_test.gleam +++ b/test/gleam/dict_test.gleam @@ -403,3 +403,29 @@ pub fn extra_keys_equality_test() { should.be_false(map1 == map2) should.be_false(map2 == map1) } + +pub fn combine_test() { + let map1 = dict.from_list([#("a", 3), #("b", 2)]) + let map2 = dict.from_list([#("a", 2), #("c", 3), #("d", 4)]) + + dict.combine(map1, map2, fn(one, other) { one - other }) + |> should.equal(dict.from_list([#("a", 1), #("b", 2), #("c", 3), #("d", 4)])) +} + +pub fn combine_with_empty_test() { + let map1 = dict.from_list([#("a", 3), #("b", 2)]) + + dict.combine(map1, dict.new(), fn(one, _) { one }) + |> should.equal(map1) + + dict.combine(dict.new(), map1, fn(one, _) { one }) + |> should.equal(map1) +} + +pub fn combine_with_no_overlapping_keys_test() { + let map1 = dict.from_list([#("a", 1), #("b", 2)]) + let map2 = dict.from_list([#("c", 3), #("d", 4)]) + + dict.combine(map1, map2, fn(one, _) { one }) + |> should.equal(dict.from_list([#("a", 1), #("b", 2), #("c", 3), #("d", 4)])) +} |