Skip to content

Commit

Permalink
changes
Browse files Browse the repository at this point in the history
  • Loading branch information
inoas committed Dec 9, 2024
1 parent 370a060 commit 1959ea6
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased

- The dict module gains a new `update` function.

## v0.46.0 - 2024-12-08

- Improved the performance of comparing two `Dict`s of equal size on the
Expand Down
57 changes: 56 additions & 1 deletion src/gleam/dict.gleam
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import gleam/option.{type Option}
import gleam/option.{type Option, None, Some}

/// A dictionary of keys and values.
///
Expand Down Expand Up @@ -450,6 +450,61 @@ pub fn upsert(
|> insert(dict, key, _)
}

/// Changes the key-value pair in a dict using a given function; in case:
///
/// 1. the `update` key is present in the dict, then the value passed to
/// function `with` is `Some(value)`; then:
/// 1. if `fun` returns `Some(value)`, then the `update` key is newly
/// associated with to the value of `value` of `Some(value)`.
/// 2. else the `update` key and its associated value are removed from the
/// dict.
/// 2. the `update` key is not present in the dict, then the `value` passed to
/// function `with` is `None`; then:
/// 1. if `fun` returns `Some(value)`, then the `update key` is added to the
/// dict associated with the `value` of `Some(value)`.
/// 2. else the `update key` is not added to the map.
///
/// ## Example
///
/// ```gleam
/// let dict = dict.from_list([#("a", 0), #("b", 1), #("c", 2)])
///
/// let inc_if_exists_or_discard = fn(x) {
/// case x {
/// Some(i) -> Some(i + 1)
/// None -> None
/// }
/// }
///
/// dict
/// |> dict.update("a", inc_if_exists_or_discard)
/// // -> from_list([#("a", 1), #("b", 1), #("c", 2)])
///
/// dict
/// |> dict.update("e", inc_if_exists_or_discard)
/// // -> from_list([#("a", 0), #("b", 1), #("c", 2)])
/// ```
///
pub fn update(
in dict: Dict(k, v),
update key: k,
with fun: fn(Option(v)) -> Option(v),
) -> Dict(k, v) {
case do_get(dict, key) {
Ok(existing_value) ->
case fun(Some(existing_value)) {
Some(value) -> do_insert(key, value, dict)
None -> do_delete(key, dict)
}
Error(_) -> {
case fun(None) {
Some(value) -> do_insert(key, value, dict)
None -> dict
}
}
}
}

/// Combines all entries into a single value by calling a given function on each
/// one.
///
Expand Down
64 changes: 64 additions & 0 deletions test/gleam/dict_test.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,70 @@ pub fn upsert_test() {
|> should.equal(dict.from_list([#("a", 0), #("b", 1), #("c", 2), #("z", 0)]))
}

pub fn update_test() {
let dict = dict.from_list([#("a", 0), #("b", 1), #("c", 2)])

let inc_or_zero = fn(x) {
case x {
Some(i) -> Some(i + 1)
None -> Some(0)
}
}

dict
|> dict.update("a", inc_or_zero)
|> should.equal(dict.from_list([#("a", 1), #("b", 1), #("c", 2)]))

dict
|> dict.update("e", inc_or_zero)
|> should.equal(dict.from_list([#("a", 0), #("b", 1), #("c", 2), #("e", 0)]))

let discard_existing_or_noop = fn(x) {
case x {
Some(_i) -> None
None -> None
}
}

dict
|> dict.update("a", discard_existing_or_noop)
|> should.equal(dict.from_list([#("b", 1), #("c", 2)]))

dict
|> dict.update("e", discard_existing_or_noop)
|> should.equal(dict.from_list([#("a", 0), #("b", 1), #("c", 2)]))

let inc_existing_or_noop = fn(x) {
case x {
Some(i) -> Some(i + 1)
None -> None
}
}

dict
|> dict.update("a", inc_existing_or_noop)
|> should.equal(dict.from_list([#("a", 1), #("b", 1), #("c", 2)]))

dict
|> dict.update("e", inc_existing_or_noop)
|> should.equal(dict.from_list([#("a", 0), #("b", 1), #("c", 2)]))

let discard_existing_or_zero = fn(x) {
case x {
Some(_i) -> None
None -> Some(0)
}
}

dict
|> dict.update("a", discard_existing_or_zero)
|> should.equal(dict.from_list([#("b", 1), #("c", 2)]))

dict
|> dict.update("e", discard_existing_or_zero)
|> should.equal(dict.from_list([#("a", 0), #("b", 1), #("c", 2), #("e", 0)]))
}

pub fn fold_test() {
let dict = dict.from_list([#("a", 0), #("b", 1), #("c", 2), #("d", 3)])

Expand Down

0 comments on commit 1959ea6

Please sign in to comment.