Skip to content

Commit f66ad56

Browse files
giacomocavalierilpil
authored andcommitted
The dict module gains the combine function
1 parent 84f5732 commit f66ad56

File tree

3 files changed

+64
-11
lines changed

3 files changed

+64
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
correctly on Erlang.
1313
- `dynamic.unsafe_coerce` function has been deprecated.
1414
- Fixed `bit_array` slices of slices sometimes being incorrect on JavaScript.
15+
- The `dict` module gains the `combine` function.
1516

1617
## v0.37.0 - 2024-04-19
1718

src/gleam/dict.gleam

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@ pub fn size(dict: Dict(k, v)) -> Int
4343
/// ## Examples
4444
///
4545
/// Calling `to_list` on an empty `dict` returns an empty list.
46-
///
46+
///
4747
/// ```gleam
4848
/// new() |> to_list
4949
/// // -> []
5050
/// ```
51-
///
52-
/// The ordering of elements in the resulting list is an implementation detail
51+
///
52+
/// The ordering of elements in the resulting list is an implementation detail
5353
/// that should not be relied upon.
5454
///
5555
/// ```gleam
@@ -498,31 +498,57 @@ pub fn fold(
498498
|> do_fold(initial, fun)
499499
}
500500

501-
/// Calls a function for each key and value in a dict, discarding the return
501+
/// Calls a function for each key and value in a dict, discarding the return
502502
/// value.
503-
///
503+
///
504504
/// Useful for producing a side effect for every item of a dict.
505-
///
505+
///
506506
/// ```gleam
507507
/// import gleam/io
508-
///
508+
///
509509
/// let dict = from_list([#("a", "apple"), #("b", "banana"), #("c", "cherry")])
510-
///
511-
/// each(dict, fn(key, value) {
510+
///
511+
/// each(dict, fn(key, value) {
512512
/// io.println(key <> " => " <> value)
513513
/// })
514514
/// // -> Nil
515515
/// // a => apple
516516
/// // b => banana
517517
/// // c => cherry
518518
/// ```
519-
///
519+
///
520520
/// The order of elements in the iteration is an implementation detail that
521521
/// should not be relied upon.
522-
///
522+
///
523523
pub fn each(dict: Dict(k, v), fun: fn(k, v) -> b) -> Nil {
524524
fold(dict, Nil, fn(nil, k, v) {
525525
fun(k, v)
526526
nil
527527
})
528528
}
529+
530+
/// Creates a new dict from a pair of given dicts by combining their entries.
531+
///
532+
/// If there are entries with the same keys in both dicts the given function is
533+
/// used to determine the new value to use in the resulting dict.
534+
///
535+
/// ## Examples
536+
///
537+
/// ```gleam
538+
/// let a = from_list([#("a", 0), #("b", 1)])
539+
/// let b = from_list([#("a", 2), #("c", 3)])
540+
/// combine(a, b, fn(one, other) { one + other })
541+
/// // -> from_list([#("a", 2), #("b", 1), #("c", 3)])
542+
/// ```
543+
///
544+
pub fn combine(
545+
dict: Dict(k, v),
546+
other: Dict(k, v),
547+
with fun: fn(v, v) -> v,
548+
) -> Dict(k, v) {
549+
use acc, key, value <- fold(over: dict, from: other)
550+
case get(acc, key) {
551+
Ok(other_value) -> insert(acc, key, fun(value, other_value))
552+
Error(_) -> insert(acc, key, value)
553+
}
554+
}

test/gleam/dict_test.gleam

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,3 +403,29 @@ pub fn extra_keys_equality_test() {
403403
should.be_false(map1 == map2)
404404
should.be_false(map2 == map1)
405405
}
406+
407+
pub fn combine_test() {
408+
let map1 = dict.from_list([#("a", 3), #("b", 2)])
409+
let map2 = dict.from_list([#("a", 2), #("c", 3), #("d", 4)])
410+
411+
dict.combine(map1, map2, fn(one, other) { one - other })
412+
|> should.equal(dict.from_list([#("a", 1), #("b", 2), #("c", 3), #("d", 4)]))
413+
}
414+
415+
pub fn combine_with_empty_test() {
416+
let map1 = dict.from_list([#("a", 3), #("b", 2)])
417+
418+
dict.combine(map1, dict.new(), fn(one, _) { one })
419+
|> should.equal(map1)
420+
421+
dict.combine(dict.new(), map1, fn(one, _) { one })
422+
|> should.equal(map1)
423+
}
424+
425+
pub fn combine_with_no_overlapping_keys_test() {
426+
let map1 = dict.from_list([#("a", 1), #("b", 2)])
427+
let map2 = dict.from_list([#("c", 3), #("d", 4)])
428+
429+
dict.combine(map1, map2, fn(one, _) { one })
430+
|> should.equal(dict.from_list([#("a", 1), #("b", 2), #("c", 3), #("d", 4)]))
431+
}

0 commit comments

Comments
 (0)