Skip to content

Commit

Permalink
The dict module gains the combine function
Browse files Browse the repository at this point in the history
  • Loading branch information
giacomocavalieri authored and lpil committed May 24, 2024
1 parent 84f5732 commit f66ad56
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
48 changes: 37 additions & 11 deletions src/gleam/dict.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -498,31 +498,57 @@ 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
/// // a => apple
/// // 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)
}
}
26 changes: 26 additions & 0 deletions test/gleam/dict_test.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -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)]))
}

0 comments on commit f66ad56

Please sign in to comment.