diff options
author | Louis Pilfold <louis@lpil.uk> | 2023-11-21 14:39:00 +0000 |
---|---|---|
committer | Louis Pilfold <louis@lpil.uk> | 2023-11-21 15:11:37 +0000 |
commit | 6139295179325810d53613879613b4181188d56a (patch) | |
tree | 819b7bd4e26a50d5e45dc5d6e638078d8d56c4cf /src | |
parent | aec149b434c1ca537c2fe9d307113c226413e035 (diff) | |
download | gleam_stdlib-6139295179325810d53613879613b4181188d56a.tar.gz gleam_stdlib-6139295179325810d53613879613b4181188d56a.zip |
map -> dict
Diffstat (limited to 'src')
-rw-r--r-- | src/gleam/dict.gleam | 548 | ||||
-rw-r--r-- | src/gleam/dynamic.gleam | 34 | ||||
-rw-r--r-- | src/gleam/iterator.gleam | 16 | ||||
-rw-r--r-- | src/gleam/list.gleam | 18 | ||||
-rw-r--r-- | src/gleam/map.gleam | 532 | ||||
-rw-r--r-- | src/gleam/set.gleam | 28 | ||||
-rw-r--r-- | src/gleam_stdlib.erl | 2 |
7 files changed, 654 insertions, 524 deletions
diff --git a/src/gleam/dict.gleam b/src/gleam/dict.gleam new file mode 100644 index 0000000..ca6d272 --- /dev/null +++ b/src/gleam/dict.gleam @@ -0,0 +1,548 @@ +import gleam/option.{type Option} + +/// A dictionary of keys and values. +/// +/// Any type can be used for the keys and values of a map, but all the keys +/// must be of the same type and all the values must be of the same type. +/// +/// Each key can only be present in a map once. +/// +/// Dicts are not ordered in any way, and any unintentional ordering is not to +/// be relied upon in your code as it may change in future versions of Erlang +/// or Gleam. +/// +/// See [the Erlang map module](https://erlang.org/doc/man/maps.html) for more +/// information. +/// +pub type Dict(key, value) + +/// Determines the number of key-value pairs in the dict. +/// This function runs in constant time and does not need to iterate the dict. +/// +/// ## Examples +/// +/// ```gleam +/// > new() |> size() +/// 0 +/// ``` +/// +/// ```gleam +/// > new() |> insert("key", "value") |> size() +/// 1 +/// ``` +/// +pub fn size(map: Dict(k, v)) -> Int { + do_size(map) +} + +@external(erlang, "maps", "size") +@external(javascript, "../gleam_stdlib.mjs", "map_size") +fn do_size(a: Dict(k, v)) -> Int + +/// Converts the map to a list of 2-element tuples `#(key, value)`, one for +/// each key-value pair in the dict. +/// +/// The tuples in the list have no specific order. +/// +/// ## Examples +/// +/// ```gleam +/// > new() |> to_list() +/// [] +/// ``` +/// +/// ```gleam +/// > new() |> insert("key", 0) |> to_list() +/// [#("key", 0)] +/// ``` +/// +pub fn to_list(map: Dict(key, value)) -> List(#(key, value)) { + do_to_list(map) +} + +@external(erlang, "maps", "to_list") +@external(javascript, "../gleam_stdlib.mjs", "map_to_list") +fn do_to_list(a: Dict(key, value)) -> List(#(key, value)) + +/// Converts a list of 2-element tuples `#(key, value)` to a dict. +/// +/// If two tuples have the same key the last one in the list will be the one +/// that is present in the dict. +/// +pub fn from_list(list: List(#(k, v))) -> Dict(k, v) { + do_from_list(list) +} + +@target(erlang) +@external(erlang, "maps", "from_list") +fn do_from_list(a: List(#(key, value))) -> Dict(key, value) + +@target(javascript) +fn fold_list_of_pair( + over list: List(#(k, v)), + from initial: Dict(k, v), +) -> Dict(k, v) { + case list { + [] -> initial + [x, ..rest] -> fold_list_of_pair(rest, insert(initial, x.0, x.1)) + } +} + +@target(javascript) +fn do_from_list(list: List(#(k, v))) -> Dict(k, v) { + fold_list_of_pair(list, new()) +} + +/// Determines whether or not a value present in the map for a given key. +/// +/// ## Examples +/// +/// ```gleam +/// > new() |> insert("a", 0) |> has_key("a") +/// True +/// ``` +/// +/// ```gleam +/// > new() |> insert("a", 0) |> has_key("b") +/// False +/// ``` +/// +pub fn has_key(map: Dict(k, v), key: k) -> Bool { + do_has_key(key, map) +} + +@target(erlang) +@external(erlang, "maps", "is_key") +fn do_has_key(a: key, b: Dict(key, v)) -> Bool + +@target(javascript) +fn do_has_key(key: k, map: Dict(k, v)) -> Bool { + get(map, key) != Error(Nil) +} + +/// Creates a fresh map that contains no values. +/// +pub fn new() -> Dict(key, value) { + do_new() +} + +@external(erlang, "maps", "new") +@external(javascript, "../gleam_stdlib.mjs", "new_map") +fn do_new() -> Dict(key, value) + +/// Fetches a value from a map for a given key. +/// +/// The map may not have a value for the key, so the value is wrapped in a +/// `Result`. +/// +/// ## Examples +/// +/// ```gleam +/// > new() |> insert("a", 0) |> get("a") +/// Ok(0) +/// ``` +/// +/// ```gleam +/// > new() |> insert("a", 0) |> get("b") +/// Error(Nil) +/// ``` +/// +pub fn get(from: Dict(key, value), get: key) -> Result(value, Nil) { + do_get(from, get) +} + +@external(erlang, "gleam_stdlib", "map_get") +@external(javascript, "../gleam_stdlib.mjs", "map_get") +fn do_get(a: Dict(key, value), b: key) -> Result(value, Nil) + +/// Inserts a value into the map with the given key. +/// +/// If the map already has a value for the given key then the value is +/// replaced with the new value. +/// +/// ## Examples +/// +/// ```gleam +/// > new() |> insert("a", 0) |> to_list +/// [#("a", 0)] +/// ``` +/// +/// ```gleam +/// > new() |> insert("a", 0) |> insert("a", 5) |> to_list +/// [#("a", 5)] +/// ``` +/// +pub fn insert(into map: Dict(k, v), for key: k, insert value: v) -> Dict(k, v) { + do_insert(key, value, map) +} + +@external(erlang, "maps", "put") +@external(javascript, "../gleam_stdlib.mjs", "map_insert") +fn do_insert(a: key, b: value, c: Dict(key, value)) -> Dict(key, value) + +/// Updates all values in a given map by calling a given function on each key +/// and value. +/// +/// ## Examples +/// +/// ```gleam +/// > [#(3, 3), #(2, 4)] +/// > |> from_list +/// > |> map_values(fn(key, value) { key * value }) +/// [#(3, 9), #(2, 8)] +/// ``` +/// +pub fn map_values(in map: Dict(k, v), with fun: fn(k, v) -> w) -> Dict(k, w) { + do_map_values(fun, map) +} + +@target(erlang) +@external(erlang, "maps", "map") +fn do_map_values(a: fn(key, value) -> b, b: Dict(key, value)) -> Dict(key, b) + +@target(javascript) +fn do_map_values(f: fn(key, value) -> b, map: Dict(key, value)) -> Dict(key, b) { + let f = fn(map, k, v) { insert(map, k, f(k, v)) } + map + |> fold(from: new(), with: f) +} + +/// Gets a list of all keys in a given dict. +/// +/// Dicts are not ordered so the keys are not returned in any specific order. Do +/// not write code that relies on the order keys are returned by this function +/// as it may change in later versions of Gleam or Erlang. +/// +/// ## Examples +/// +/// ```gleam +/// > keys([#("a", 0), #("b", 1)]) +/// ["a", "b"] +/// ``` +/// +pub fn keys(map: Dict(keys, v)) -> List(keys) { + do_keys(map) +} + +@target(erlang) +@external(erlang, "maps", "keys") +fn do_keys(a: Dict(keys, v)) -> List(keys) + +@target(javascript) +fn reverse_and_concat(remaining, accumulator) { + case remaining { + [] -> accumulator + [item, ..rest] -> reverse_and_concat(rest, [item, ..accumulator]) + } +} + +@target(javascript) +fn do_keys_acc(list: List(#(k, v)), acc: List(k)) -> List(k) { + case list { + [] -> reverse_and_concat(acc, []) + [x, ..xs] -> do_keys_acc(xs, [x.0, ..acc]) + } +} + +@target(javascript) +fn do_keys(map: Dict(k, v)) -> List(k) { + let list_of_pairs = + map + |> to_list + do_keys_acc(list_of_pairs, []) +} + +/// Gets a list of all values in a given dict. +/// +/// Dicts are not ordered so the values are not returned in any specific order. Do +/// not write code that relies on the order values are returned by this function +/// as it may change in later versions of Gleam or Erlang. +/// +/// ## Examples +/// +/// ```gleam +/// > values(from_list([#("a", 0), #("b", 1)])) +/// [0, 1] +/// ``` +/// +pub fn values(map: Dict(k, values)) -> List(values) { + do_values(map) +} + +@target(erlang) +@external(erlang, "maps", "values") +fn do_values(a: Dict(k, values)) -> List(values) + +@target(javascript) +fn do_values_acc(list: List(#(k, v)), acc: List(v)) -> List(v) { + case list { + [] -> reverse_and_concat(acc, []) + [x, ..xs] -> do_values_acc(xs, [x.1, ..acc]) + } +} + +@target(javascript) +fn do_values(map: Dict(k, v)) -> List(v) { + let list_of_pairs = + map + |> to_list + do_values_acc(list_of_pairs, []) +} + +/// Creates a new map from a given map, minus any entries that a given function +/// returns `False` for. +/// +/// ## Examples +/// +/// ```gleam +/// > from_list([#("a", 0), #("b", 1)]) +/// > |> filter(fn(key, value) { value != 0 }) +/// from_list([#("b", 1)]) +/// ``` +/// +/// ```gleam +/// > from_list([#("a", 0), #("b", 1)]) +/// > |> filter(fn(key, value) { True }) +/// from_list([#("a", 0), #("b", 1)]) +/// ``` +/// +pub fn filter( + in map: Dict(k, v), + keeping predicate: fn(k, v) -> Bool, +) -> Dict(k, v) { + do_filter(predicate, map) +} + +@target(erlang) +@external(erlang, "maps", "filter") +fn do_filter(a: fn(key, value) -> Bool, b: Dict(key, value)) -> Dict(key, value) + +@target(javascript) +fn do_filter( + f: fn(key, value) -> Bool, + map: Dict(key, value), +) -> Dict(key, value) { + let insert = fn(map, k, v) { + case f(k, v) { + True -> insert(map, k, v) + _ -> map + } + } + map + |> fold(from: new(), with: insert) +} + +/// Creates a new map from a given map, only including any entries for which the +/// keys are in a given list. +/// +/// ## Examples +/// +/// ```gleam +/// > from_list([#("a", 0), #("b", 1)]) +/// > |> take(["b"]) +/// from_list([#("b", 1)]) +/// ``` +/// +/// ```gleam +/// > from_list([#("a", 0), #("b", 1)]) +/// > |> take(["a", "b", "c"]) +/// from_list([#("a", 0), #("b", 1)]) +/// ``` +/// +pub fn take(from map: Dict(k, v), keeping desired_keys: List(k)) -> Dict(k, v) { + do_take(desired_keys, map) +} + +@target(erlang) +@external(erlang, "maps", "with") +fn do_take(a: List(k), b: Dict(k, v)) -> Dict(k, v) + +@target(javascript) +fn insert_taken( + map: Dict(k, v), + desired_keys: List(k), + acc: Dict(k, v), +) -> Dict(k, v) { + let insert = fn(taken, key) { + case get(map, key) { + Ok(value) -> insert(taken, key, value) + _ -> taken + } + } + case desired_keys { + [] -> acc + [x, ..xs] -> insert_taken(map, xs, insert(acc, x)) + } +} + +@target(javascript) +fn do_take(desired_keys: List(k), map: Dict(k, v)) -> Dict(k, v) { + insert_taken(map, desired_keys, new()) +} + +/// Creates a new map from a pair of given maps by combining their entries. +/// +/// If there are entries with the same keys in both maps the entry from the +/// second map takes precedence. +/// +/// ## Examples +/// +/// ```gleam +/// > let a = from_list([#("a", 0), #("b", 1)]) +/// > let b = from_list([#("b", 2), #("c", 3)]) +/// > merge(a, b) +/// from_list([#("a", 0), #("b", 2), #("c", 3)]) +/// ``` +/// +pub fn merge(into map: Dict(k, v), from new_entries: Dict(k, v)) -> Dict(k, v) { + do_merge(map, new_entries) +} + +@target(erlang) +@external(erlang, "maps", "merge") +fn do_merge(a: Dict(k, v), b: Dict(k, v)) -> Dict(k, v) + +@target(javascript) +fn insert_pair(map: Dict(k, v), pair: #(k, v)) -> Dict(k, v) { + insert(map, pair.0, pair.1) +} + +@target(javascript) +fn fold_inserts(new_entries: List(#(k, v)), map: Dict(k, v)) -> Dict(k, v) { + case new_entries { + [] -> map + [x, ..xs] -> fold_inserts(xs, insert_pair(map, x)) + } +} + +@target(javascript) +fn do_merge(map: Dict(k, v), new_entries: Dict(k, v)) -> Dict(k, v) { + new_entries + |> to_list + |> fold_inserts(map) +} + +/// Creates a new map from a given map with all the same entries except for the +/// one with a given key, if it exists. +/// +/// ## Examples +/// +/// ```gleam +/// > delete([#("a", 0), #("b", 1)], "a") +/// from_list([#("b", 1)]) +/// ``` +/// +/// ```gleam +/// > delete([#("a", 0), #("b", 1)], "c") +/// from_list([#("a", 0), #("b", 1)]) +/// ``` +/// +pub fn delete(from map: Dict(k, v), delete key: k) -> Dict(k, v) { + do_delete(key, map) +} + +@external(erlang, "maps", "remove") +@external(javascript, "../gleam_stdlib.mjs", "map_remove") +fn do_delete(a: k, b: Dict(k, v)) -> Dict(k, v) + +/// Creates a new map from a given map with all the same entries except any with +/// keys found in a given list. +/// +/// ## Examples +/// +/// ```gleam +/// > drop([#("a", 0), #("b", 1)], ["a"]) +/// from_list([#("b", 2)]) +/// ``` +/// +/// ```gleam +/// > delete([#("a", 0), #("b", 1)], ["c"]) +/// from_list([#("a", 0), #("b", 1)]) +/// ``` +/// +/// ```gleam +/// > drop([#("a", 0), #("b", 1)], ["a", "b", "c"]) +/// from_list([]) +/// ``` +/// +pub fn drop(from map: Dict(k, v), drop disallowed_keys: List(k)) -> Dict(k, v) { + case disallowed_keys { + [] -> map + [x, ..xs] -> drop(delete(map, x), xs) + } +} + +/// Creates a new map with one entry updated using a given function. +/// +/// If there was not an entry in the map for the given key then the function +/// gets `None` as its argument, otherwise it gets `Some(value)`. +/// +/// ## Example +/// +/// ```gleam +/// > let increment = fn(x) { +/// > case x { +/// > Some(i) -> i + 1 +/// > None -> 0 +/// > } +/// > } +/// > let map = from_list([#("a", 0)]) +/// > +/// > update(map, "a", increment) +/// from_list([#("a", 1)]) +/// ``` +/// +/// ```gleam +/// > update(map, "b", increment) +/// from_list([#("a", 0), #("b", 0)]) +/// ``` +/// +pub fn update( + in map: Dict(k, v), + update key: k, + with fun: fn(Option(v)) -> v, +) -> Dict(k, v) { + map + |> get(key) + |> option.from_result + |> fun + |> insert(map, key, _) +} + +fn do_fold(list: List(#(k, v)), initial: acc, fun: fn(acc, k, v) -> acc) -> acc { + case list { + [] -> initial + [#(k, v), ..rest] -> do_fold(rest, fun(initial, k, v), fun) + } +} + +/// Combines all entries into a single value by calling a given function on each +/// one. +/// +/// Dicts are not ordered so the values are not returned in any specific order. Do +/// not write code that relies on the order entries are used by this function +/// as it may change in later versions of Gleam or Erlang. +/// +/// # Examples +/// +/// ```gleam +/// > let map = from_list([#("a", 1), #("b", 3), #("c", 9)]) +/// > fold(map, 0, fn(accumulator, key, value) { accumulator + value }) +/// 13 +/// ``` +/// +/// ```gleam +/// > import gleam/string.{append} +/// > fold(map, "", fn(accumulator, key, value) { append(accumulator, key) }) +/// "abc" +/// ``` +/// +pub fn fold( + over map: Dict(k, v), + from initial: acc, + with fun: fn(acc, k, v) -> acc, +) -> acc { + map + |> to_list + |> do_fold(initial, fun) +} diff --git a/src/gleam/dynamic.gleam b/src/gleam/dynamic.gleam index de8cc9e..c8bf069 100644 --- a/src/gleam/dynamic.gleam +++ b/src/gleam/dynamic.gleam @@ -1,6 +1,6 @@ import gleam/int import gleam/list -import gleam/map.{type Map} +import gleam/dict.{type Dict} import gleam/option.{type Option} import gleam/result import gleam/string_builder @@ -400,9 +400,9 @@ fn decode_optional(a: Dynamic, b: Decoder(a)) -> Result(Option(a), DecodeErrors) /// ## Examples /// /// ```gleam -/// > import gleam/map -/// > map.new() -/// > |> map.insert("Hello", "World") +/// > import gleam/dict +/// > dict.new() +/// > |> dict.insert("Hello", "World") /// > |> from /// > |> field(named: "Hello", of: string) /// Ok("World") @@ -434,17 +434,17 @@ pub fn field(named name: a, of inner_type: Decoder(t)) -> Decoder(t) { /// ## Examples /// /// ```gleam -/// > import gleam/map -/// > map.new() -/// > |> map.insert("Hello", "World") +/// > import gleam/dict +/// > dict.new() +/// > |> dict.insert("Hello", "World") /// > |> from /// > |> field(named: "Hello", of: string) /// Ok(Some("World")) /// ``` /// /// ```gleam -/// > import gleam/map -/// > map.new() +/// > import gleam/dict +/// > dict.new() /// > |> from /// > |> field(named: "Hello", of: string) /// Ok(None) @@ -931,14 +931,14 @@ pub fn tuple6( } } -/// Checks to see if a `Dynamic` value is a map. +/// Checks to see if a `Dynamic` value is a dict. /// /// ## Examples /// /// ```gleam -/// > import gleam/map -/// > map.new() |> from |> map(string, int) -/// Ok(map.new()) +/// > import gleam/dict +/// > dict.new() |> from |> map(string, int) +/// Ok(dict.new()) /// ``` /// /// ```gleam @@ -954,12 +954,12 @@ pub fn tuple6( pub fn map( of key_type: Decoder(k), to value_type: Decoder(v), -) -> Decoder(Map(k, v)) { +) -> Decoder(Dict(k, v)) { fn(value) { use map <- result.try(decode_map(value)) use pairs <- result.try( map - |> map.to_list + |> dict.to_list |> list.try_map(fn(pair) { let #(k, v) = pair use k <- result.try( @@ -973,13 +973,13 @@ pub fn map( Ok(#(k, v)) }), ) - Ok(map.from_list(pairs)) + Ok(dict.from_list(pairs)) } } @external(erlang, "gleam_stdlib", "decode_map") @external(javascript, "../gleam_stdlib.mjs", "decode_map") -fn decode_map(a: Dynamic) -> Result(Map(Dynamic, Dynamic), DecodeErrors) +fn decode_map(a: Dynamic) -> Result(Dict(Dynamic, Dynamic), DecodeErrors) /// Joins multiple decoders into one. When run they will each be tried in turn /// until one succeeds, or they all fail. diff --git a/src/gleam/iterator.gleam b/src/gleam/iterator.gleam index 4e9d82a..c57e7fd 100644 --- a/src/gleam/iterator.gleam +++ b/src/gleam/iterator.gleam @@ -1,7 +1,7 @@ import gleam/result import gleam/int import gleam/list -import gleam/map.{type Map} +import gleam/dict.{type Dict} import gleam/option.{type Option, None, Some} import gleam/order @@ -1161,14 +1161,14 @@ fn update_group_with(el: element) -> fn(Option(List(element))) -> List(element) fn group_updater( f: fn(element) -> key, -) -> fn(Map(key, List(element)), element) -> Map(key, List(element)) { +) -> fn(Dict(key, List(element)), element) -> Dict(key, List(element)) { fn(groups, elem) { groups - |> map.update(f(elem), update_group_with(elem)) + |> dict.update(f(elem), update_group_with(elem)) } } -/// Returns a `Map(k, List(element))` of elements from the given iterator +/// Returns a `Dict(k, List(element))` of elements from the given iterator /// grouped with the given key function. /// /// The order within each group is preserved from the iterator. @@ -1177,16 +1177,16 @@ fn group_updater( /// /// ```gleam /// > from_list([1, 2, 3, 4, 5, 6]) |> group(by: fn(n) { n % 3 }) -/// map.from_list([#(0, [3, 6]), #(1, [1, 4]), #(2, [2, 5])]) +/// dict.from_list([#(0, [3, 6]), #(1, [1, 4]), #(2, [2, 5])]) /// ``` /// pub fn group( in iterator: Iterator(element), by key: fn(element) -> key, -) -> Map(key, List(element)) { +) -> Dict(key, List(element)) { iterator - |> fold(map.new(), group_updater(key)) - |> map.map_values(fn(_, group) { list.reverse(group) }) + |> fold(dict.new(), group_updater(key)) + |> dict.map_values(fn(_, group) { list.reverse(group) }) } /// This function acts similar to fold, but does not take an initial state. diff --git a/src/gleam/list.gleam b/src/gleam/list.gleam index 0b4c627..af206c9 100644 --- a/src/gleam/list.gleam +++ b/src/gleam/list.gleam @@ -26,7 +26,7 @@ import gleam/int import gleam/float import gleam/order.{type Order} import gleam/pair -import gleam/map.{type Map} +import gleam/dict.{type Dict} /// An error value returned by the `strict_zip` function. /// @@ -249,11 +249,11 @@ pub fn rest(list: List(a)) -> Result(List(a), Nil) { fn update_group( f: fn(element) -> key, -) -> fn(Map(key, List(element)), element) -> Map(key, List(element)) { +) -> fn(Dict(key, List(element)), element) -> Dict(key, List(element)) { fn(groups, elem) { - case map.get(groups, f(elem)) { - Ok(existing) -> map.insert(groups, f(elem), [elem, ..existing]) - Error(_) -> map.insert(groups, f(elem), [elem]) + case dict.get(groups, f(elem)) { + Ok(existing) -> dict.insert(groups, f(elem), [elem, ..existing]) + Error(_) -> dict.insert(groups, f(elem), [elem]) } } } @@ -273,7 +273,7 @@ fn update_group( /// Error(_) -> "Failed" /// } /// }) -/// |> map.to_list +/// |> dict.to_list /// /// [ /// #("Failed", [Error("Wrong")]), @@ -281,12 +281,12 @@ fn update_group( /// ] /// /// > group([1,2,3,4,5], by: fn(i) { i - i / 3 * 3 }) -/// |> map.to_list +/// |> dict.to_list /// [#(0, [3]), #(1, [4, 1]), #(2, [5, 2])] /// ``` /// -pub fn group(list: List(v), by key: fn(v) -> k) -> Map(k, List(v)) { - fold(list, map.new(), update_group(key)) +pub fn group(list: List(v), by key: fn(v) -> k) -> Dict(k, List(v)) { + fold(list, dict.new(), update_group(key)) } fn do_filter(list: List(a), fun: fn(a) -> Bool, acc: List(a)) -> List(a) { diff --git a/src/gleam/map.gleam b/src/gleam/map.gleam index 21ac4f8..1f8b228 100644 --- a/src/gleam/map.gleam +++ b/src/gleam/map.gleam @@ -1,233 +1,55 @@ import gleam/option.{type Option} +import gleam/dict -/// A dictionary of keys and values. -/// -/// Any type can be used for the keys and values of a map, but all the keys -/// must be of the same type and all the values must be of the same type. -/// -/// Each key can only be present in a map once. -/// -/// Maps are not ordered in any way, and any unintentional ordering is not to -/// be relied upon in your code as it may change in future versions of Erlang -/// or Gleam. -/// -/// See [the Erlang map module](https://erlang.org/doc/man/maps.html) for more -/// information. -/// -pub type Map(key, value) +@deprecated("Please use the `gleam/dict` module instead") +pub type Map(key, value) = + dict.Dict(key, value) -/// Determines the number of key-value pairs in the map. -/// This function runs in constant time and does not need to iterate the map. -/// -/// ## Examples -/// -/// ```gleam -/// > new() |> size() -/// 0 -/// ``` -/// -/// ```gleam -/// > new() |> insert("key", "value") |> size() -/// 1 -/// ``` -/// -pub fn size(map: Map(k, v)) -> Int { - do_size(map) +@deprecated("Please use the `gleam/dict` module instead") +pub fn size(map) -> Int { + dict.size(map) } -@external(erlang, "maps", "size") -@external(javascript, "../gleam_stdlib.mjs", "map_size") -fn do_size(a: Map(k, v)) -> Int - -/// Converts the map to a list of 2-element tuples `#(key, value)`, one for -/// each key-value pair in the map. -/// -/// The tuples in the list have no specific order. -/// -/// ## Examples -/// -/// ```gleam -/// > new() |> to_list() -/// [] -/// ``` -/// -/// ```gleam -/// > new() |> insert("key", 0) |> to_list() -/// [#("key", 0)] -/// ``` -/// -pub fn to_list(map: Map(key, value)) -> List(#(key, value)) { - do_to_list(map) +@deprecated("Please use the `gleam/dict` module instead") +pub fn to_list(map) -> List(#(key, value)) { + dict.to_list(map) } -@external(erlang, "maps", "to_list") -@external(javascript, "../gleam_stdlib.mjs", "map_to_list") -fn do_to_list(a: Map(key, value)) -> List(#(key, value)) - -/// Converts a list of 2-element tuples `#(key, value)` to a map. -/// -/// If two tuples have the same key the last one in the list will be the one -/// that is present in the map. -/// -pub fn from_list(list: List(#(k, v))) -> Map(k, v) { - do_from_list(list) -} - -@target(erlang) -@external(erlang, "maps", "from_list") -fn do_from_list(a: List(#(key, value))) -> Map(key, value) - -@target(javascript) -fn fold_list_of_pair( - over list: List(#(k, v)), - from initial: Map(k, v), -) -> Map(k, v) { - case list { - [] -> initial - [x, ..rest] -> fold_list_of_pair(rest, insert(initial, x.0, x.1)) - } -} - -@target(javascript) -fn do_from_list(list: List(#(k, v))) -> Map(k, v) { - fold_list_of_pair(list, new()) -} - -/// Determines whether or not a value present in the map for a given key. -/// -/// ## Examples -/// -/// ```gleam -/// > new() |> insert("a", 0) |> has_key("a") -/// True -/// ``` -/// -/// ```gleam -/// > new() |> insert("a", 0) |> has_key("b") -/// False -/// ``` -/// -pub fn has_key(map: Map(k, v), key: k) -> Bool { - do_has_key(key, map) -} - -@target(erlang) -@external(erlang, "maps", "is_key") -fn do_has_key(a: key, b: Map(key, v)) -> Bool - -@target(javascript) -fn do_has_key(key: k, map: Map(k, v)) -> Bool { - get(map, key) != Error(Nil) +@deprecated("Please use the `gleam/dict` module instead") +pub fn from_list(list: List(#(k, v))) { + dict.from_list(list) } -/// Creates a fresh map that contains no values. -/// -pub fn new() -> Map(key, value) { - do_new() +@deprecated("Please use the `gleam/dict` module instead") +pub fn has_key(map, key: k) -> Bool { + dict.has_key(map, key) } -@external(erlang, "maps", "new") -@external(javascript, "../gleam_stdlib.mjs", "new_map") -fn do_new() -> Map(key, value) - -/// Fetches a value from a map for a given key. -/// -/// The map may not have a value for the key, so the value is wrapped in a -/// `Result`. -/// -/// ## Examples -/// -/// ```gleam -/// > new() |> insert("a", 0) |> get("a") -/// Ok(0) -/// ``` -/// -/// ```gleam -/// > new() |> insert("a", 0) |> get("b") -/// Error(Nil) -/// ``` -/// -pub fn get(from: Map(key, value), get: key) -> Result(value, Nil) { - do_get(from, get) +@deprecated("Please use the `gleam/dict` module instead") +pub fn new() { + dict.new() } -@external(erlang, "gleam_stdlib", "map_get") -@external(javascript, "../gleam_stdlib.mjs", "map_get") -fn do_get(a: Map(key, value), b: key) -> Result(value, Nil) - -/// Inserts a value into the map with the given key. -/// -/// If the map already has a value for the given key then the value is -/// replaced with the new value. -/// -/// ## Examples -/// -/// ```gleam -/// > new() |> insert("a", 0) |> to_list -/// [#("a", 0)] -/// ``` -/// -/// ```gleam -/// > new() |> insert("a", 0) |> insert("a", 5) |> to_list -/// [#("a", 5)] -/// ``` -/// -pub fn insert(into map: Map(k, v), for key: k, insert value: v) -> Map(k, v) { - do_insert(key, value, map) +@deprecated("Please use the `gleam/dict` module instead") +pub fn get(from, get: key) -> Result(value, Nil) { + dict.get(from, get) } -@external(erlang, "maps", "put") -@external(javascript, "../gleam_stdlib.mjs", "map_insert") -fn do_insert(a: key, b: value, c: Map(key, value)) -> Map(key, value) - -/// Updates all values in a given map by calling a given function on each key -/// and value. -/// -/// ## Examples -/// -/// ```gleam -/// > [#(3, 3), #(2, 4)] -/// > |> from_list -/// > |> map_values(fn(key, value) { key * value }) -/// [#(3, 9), #(2, 8)] -/// ``` -/// -pub fn map_values(in map: Map(k, v), with fun: fn(k, v) -> w) -> Map(k, w) { - do_map_values(fun, map) +@deprecated("Please use the `gleam/dict` module instead") +pub fn insert(into map, for key: k, insert value: v) { + dict.insert(map, key, value) } -@target(erlang) -@external(erlang, "maps", "map") -fn do_map_values(a: fn(key, value) -> b, b: Map(key, value)) -> Map(key, b) - -@target(javascript) -fn do_map_values(f: fn(key, value) -> b, map: Map(key, value)) -> Map(key, b) { - let f = fn(map, k, v) { insert(map, k, f(k, v)) } - map - |> fold(from: new(), with: f) +@deprecated("Please use the `gleam/dict` module instead") +pub fn map_values(in map, with fun: fn(k, v) -> w) { + dict.map_values(map, fun) } -/// Gets a list of all keys in a given map. -/// -/// Maps are not ordered so the keys are not returned in any specific order. Do -/// not write code that relies on the order keys are returned by this function -/// as it may change in later versions of Gleam or Erlang. -/// -/// ## Examples -/// -/// ```gleam -/// > keys([#("a", 0), #("b", 1)]) -/// ["a", "b"] -/// ``` -/// -pub fn keys(map: Map(keys, v)) -> List(keys) { - do_keys(map) +@deprecated("Please use the `gleam/dict` module instead") +pub fn keys(map) -> List(keys) { + dict.keys(map) } -@target(erlang) -@external(erlang, "maps", "keys") -fn do_keys(a: Map(keys, v)) -> List(keys) - @target(javascript) fn reverse_and_concat(remaining, accumulator) { case remaining { @@ -245,80 +67,25 @@ fn do_keys_acc(list: List(#(k, v)), acc: List(k)) -> List(k) { } @target(javascript) -fn do_keys(map: Map(k, v)) -> List(k) { +fn do_keys(map) -> List(k) { let list_of_pairs = map |> to_list do_keys_acc(list_of_pairs, []) } -/// Gets a list of all values in a given map. -/// -/// Maps are not ordered so the values are not returned in any specific order. Do -/// not write code that relies on the order values are returned by this function -/// as it may change in later versions of Gleam or Erlang. -/// -/// ## Examples -/// -/// ```gleam -/// > values(from_list([#("a", 0), #("b", 1)])) -/// [0, 1] -/// ``` -/// -pub fn values(map: Map(k, values)) -> List(values) { - do_values(map) +@deprecated("Please use the `gleam/dict` module instead") +pub fn values(map) -> List(values) { + dict.values(map) } -@target(erlang) -@external(erlang, "maps", "values") -fn do_values(a: Map(k, values)) -> List(values) - -@target(javascript) -fn do_values_acc(list: List(#(k, v)), acc: List(v)) -> List(v) { - case list { - [] -> reverse_and_concat(acc, []) - [x, ..xs] -> do_values_acc(xs, [x.1, ..acc]) - } +@deprecated("Please use the `gleam/dict` module instead") +pub fn filter(in map, keeping predicate: fn(k, v) -> Bool) { + dict.filter(map, predicate) } @target(javascript) -fn do_values(map: Map(k, v)) -> List(v) { - let list_of_pairs = - map - |> to_list - do_values_acc(list_of_pairs, []) -} - -/// Creates a new map from a given map, minus any entries that a given function -/// returns `False` for. -/// -/// ## Examples -/// -/// ```gleam -/// > from_list([#("a", 0), #("b", 1)]) -/// > |> filter(fn(key, value) { value != 0 }) -/// from_list([#("b", 1)]) -/// ``` -/// -/// ```gleam -/// > from_list([#("a", 0), #("b", 1)]) -/// > |> filter(fn(key, value) { True }) -/// from_list([#("a", 0), #("b", 1)]) -/// ``` -/// -pub fn filter( - in map: Map(k, v), - keeping predicate: fn(k, v) -> Bool, -) -> Map(k, v) { - do_filter(predicate, map) -} - -@target(erlang) -@external(erlang, "maps", "filter") -fn do_filter(a: fn(key, value) -> Bool, b: Map(key, value)) -> Map(key, value) - -@target(javascript) -fn do_filter(f: fn(key, value) -> Bool, map: Map(key, value)) -> Map(key, value) { +fn do_filter(f: fn(key, value) -> Bool, map) { let insert = fn(map, k, v) { case f(k, v) { True -> insert(map, k, v) @@ -329,217 +96,32 @@ fn do_filter(f: fn(key, value) -> Bool, map: Map(key, value)) -> Map(key, value) |> fold(from: new(), with: insert) } -/// Creates a new map from a given map, only including any entries for which the -/// keys are in a given list. -/// -/// ## Examples -/// -/// ```gleam -/// > from_list([#("a", 0), #("b", 1)]) -/// > |> take(["b"]) -/// from_list([#("b", 1)]) -/// ``` -/// -/// ```gleam -/// > from_list([#("a", 0), #("b", 1)]) -/// > |> take(["a", "b", "c"]) -/// from_list([#("a", 0), #("b", 1)]) -/// ``` -/// -pub fn take(from map: Map(k, v), keeping desired_keys: List(k)) -> Map(k, v) { - do_take(desired_keys, map) +@deprecated("Please use the `gleam/dict` module instead") +pub fn take(from map, keeping desired_keys: List(k)) { + dict.take(map, desired_keys) } -@target(erlang) -@external(erlang, "maps", "with") -fn do_take(a: List(k), b: Map(k, v)) -> Map(k, v) - -@target(javascript) -fn insert_taken( - map: Map(k, v), - desired_keys: List(k), - acc: Map(k, v), -) -> Map(k, v) { - let insert = fn(taken, key) { - case get(map, key) { - Ok(value) -> insert(taken, key, value) - _ -> taken - } - } - case desired_keys { - [] -> acc - [x, ..xs] -> insert_taken(map, xs, insert(acc, x)) - } +@deprecated("Please use the `gleam/dict` module instead") +pub fn merge(into map, from new_entries) { + dict.merge(map, new_entries) } -@target(javascript) -fn do_take(desired_keys: List(k), map: Map(k, v)) -> Map(k, v) { - insert_taken(map, desired_keys, new()) -} - -/// Creates a new map from a pair of given maps by combining their entries. -/// -/// If there are entries with the same keys in both maps the entry from the -/// second map takes precedence. -/// -/// ## Examples -/// -/// ```gleam -/// > let a = from_list([#("a", 0), #("b", 1)]) -/// > let b = from_list([#("b", 2), #("c", 3)]) -/// > merge(a, b) -/// from_list([#("a", 0), #("b", 2), #("c", 3)]) -/// ``` -/// -pub fn merge(into map: Map(k, v), from new_entries: Map(k, v)) -> Map(k, v) { - do_merge(map, new_entries) +@deprecated("Please use the `gleam/dict` module instead") +pub fn delete(from map, delete key: k) { + dict.delete(map, key) } -@target(erlang) -@external(erlang, "maps", "merge") -fn do_merge(a: Map(k, v), b: Map(k, v)) -> Map(k, v) - -@target(javascript) -fn insert_pair(map: Map(k, v), pair: #(k, v)) -> Map(k, v) { - insert(map, pair.0, pair.1) +@deprecated("Please use the `gleam/dict` module instead") +pub fn drop(from map, drop disallowed_keys: List(k)) { + dict.drop(map, disallowed_keys) } -@target(javascript) -fn fold_inserts(new_entries: List(#(k, v)), map: Map(k, v)) -> Map(k, v) { - case new_entries { - [] -> map - [x, ..xs] -> fold_inserts(xs, insert_pair(map, x)) - } +@deprecated("Please use the `gleam/dict` module instead") +pub fn update(in map, update key: k, with fun: fn(Option(v)) -> v) { + dict.update(map, key, fun) } -@target(javascript) -fn do_merge(map: Map(k, v), new_entries: Map(k, v)) -> Map(k, v) { - new_entries - |> to_list - |> fold_inserts(map) -} - -/// Creates a new map from a given map with all the same entries except for the -/// one with a given key, if it exists. -/// -/// ## Examples -/// -/// ```gleam -/// > delete([#("a", 0), #("b", 1)], "a") -/// from_list([#("b", 1)]) -/// ``` -/// -/// ```gleam -/// > delete([#("a", 0), #("b", 1)], "c") -/// from_list([#("a", 0), #("b", 1)]) -/// ``` -/// -pub fn delete(from map: Map(k, v), delete key: k) -> Map(k, v) { - do_delete(key, map) -} - -@external(erlang, "maps", "remove") -@external(javascript, "../gleam_stdlib.mjs", "map_remove") -fn do_delete(a: k, b: Map(k, v)) -> Map(k, v) - -/// Creates a new map from a given map with all the same entries except any with -/// keys found in a given list. -/// -/// ## Examples -/// -/// ```gleam -/// > drop([#("a", 0), #("b", 1)], ["a"]) -/// from_list([#("b", 2)]) -/// ``` -/// -/// ```gleam -/// > delete([#("a", 0), #("b", 1)], ["c"]) -/// from_list([#("a", 0), #("b", 1)]) -/// ``` -/// -/// ```gleam -/// > drop([#("a", 0), #("b", 1)], ["a", "b", "c"]) -/// from_list([]) -/// ``` -/// -pub fn drop(from map: Map(k, v), drop disallowed_keys: List(k)) -> Map(k, v) { - case disallowed_keys { - [] -> map - [x, ..xs] -> drop(delete(map, x), xs) - } -} - -/// Creates a new map with one entry updated using a given function. -/// -/// If there was not an entry in the map for the given key then the function -/// gets `None` as its argument, otherwise it gets `Some(value)`. -/// -/// ## Example -/// -/// ```gleam -/// > let increment = fn(x) { -/// > case x { -/// > Some(i) -> i + 1 -/// > None -> 0 -/// > } -/// > } -/// > let map = from_list([#("a", 0)]) -/// > -/// > update(map, "a", increment) -/// from_list([#("a", 1)]) -/// ``` -/// -/// ```gleam -/// > update(map, "b", increment) -/// from_list([#("a", 0), #("b", 0)]) -/// ``` -/// -pub fn update( - in map: Map(k, v), - update key: k, - with fun: fn(Option(v)) -> v, -) -> Map(k, v) { - map - |> get(key) - |> option.from_result - |> fun - |> insert(map, key, _) -} - -fn do_fold(list: List(#(k, v)), initial: acc, fun: fn(acc, k, v) -> acc) -> acc { - case list { - [] -> initial - [#(k, v), ..rest] -> do_fold(rest, fun(initial, k, v), fun) - } -} - -/// Combines all entries into a single value by calling a given function on each -/// one. -/// -/// Maps are not ordered so the values are not returned in any specific order. Do -/// not write code that relies on the order entries are used by this function -/// as it may change in later versions of Gleam or Erlang. -/// -/// # Examples -/// -/// ```gleam -/// > let map = from_list([#("a", 1), #("b", 3), #("c", 9)]) -/// > fold(map, 0, fn(accumulator, key, value) { accumulator + value }) -/// 13 -/// ``` -/// -/// ```gleam -/// > import gleam/string.{append} -/// > fold(map, "", fn(accumulator, key, value) { append(accumulator, key) }) -/// "abc" -/// ``` -/// -pub fn fold( - over map: Map(k, v), - from initial: acc, - with fun: fn(acc, k, v) -> acc, -) -> acc { - map - |> to_list - |> do_fold(initial, fun) +@deprecated("Please use the `gleam/dict` module instead") +pub fn fold(over map, from initial: acc, with fun: fn(acc, k, v) -> acc) -> acc { + dict.fold(map, initial, fun) } diff --git a/src/gleam/set.gleam b/src/gleam/set.gleam index 8e33e37..df8d500 100644 --- a/src/gleam/set.gleam +++ b/src/gleam/set.gleam @@ -1,5 +1,5 @@ import gleam/list -import gleam/map.{type Map} +import gleam/dict.{type Dict} import gleam/result // A list is used as the map value as an empty list has the smallest @@ -24,13 +24,13 @@ const token = Nil /// logarithmic time complexity. /// pub opaque type Set(member) { - Set(map: Map(member, Token)) + Set(map: Dict(member, Token)) } /// Creates a new empty set. /// pub fn new() -> Set(member) { - Set(map.new()) + Set(dict.new()) } /// Gets the number of members in a set. @@ -48,7 +48,7 @@ pub fn new() -> Set(member) { /// ``` /// pub fn size(set: Set(member)) -> Int { - map.size(set.map) + dict.size(set.map) } /// Inserts an member into the set. @@ -66,7 +66,7 @@ pub fn size(set: Set(member)) -> Int { /// ``` /// pub fn insert(into set: Set(member), this member: member) -> Set(member) { - Set(map: map.insert(set.map, member, token)) + Set(map: dict.insert(set.map, member, token)) } /// Checks whether a set contains a given member. @@ -91,7 +91,7 @@ pub fn insert(into set: Set(member), this member: member) -> Set(member) { /// pub fn contains(in set: Set(member), this member: member) -> Bool { set.map - |> map.get(member) + |> dict.get(member) |> result.is_ok } @@ -111,7 +111,7 @@ pub fn contains(in set: Set(member), this member: member) -> Bool { /// ``` /// pub fn delete(from set: Set(member), this member: member) -> Set(member) { - Set(map: map.delete(set.map, member)) + Set(map: dict.delete(set.map, member)) } /// Converts the set into a list of the contained members. @@ -129,7 +129,7 @@ pub fn delete(from set: Set(member), this member: member) -> Set(member) { /// ``` /// pub fn to_list(set: Set(member)) -> List(member) { - map.keys(set.map) + dict.keys(set.map) } /// Creates a new set of the members in a given list. @@ -148,8 +148,8 @@ pub fn from_list(members: List(member)) -> Set(member) { let map = list.fold( over: members, - from: map.new(), - with: fn(m, k) { map.insert(m, k, token) }, + from: dict.new(), + with: fn(m, k) { dict.insert(m, k, token) }, ) Set(map) } @@ -174,7 +174,7 @@ pub fn fold( from initial: acc, with reducer: fn(acc, member) -> acc, ) -> acc { - map.fold(over: set.map, from: initial, with: fn(a, k, _) { reducer(a, k) }) + dict.fold(over: set.map, from: initial, with: fn(a, k, _) { reducer(a, k) }) } /// Creates a new set from an existing set, minus any members that a given @@ -196,7 +196,7 @@ pub fn filter( in set: Set(member), keeping predicate: fn(member) -> Bool, ) -> Set(member) { - Set(map.filter(in: set.map, keeping: fn(m, _) { predicate(m) })) + Set(dict.filter(in: set.map, keeping: fn(m, _) { predicate(m) })) } pub fn drop(from set: Set(member), drop disallowed: List(member)) -> Set(member) { @@ -218,11 +218,11 @@ pub fn drop(from set: Set(member), drop disallowed: List(member)) -> Set(member) /// ``` /// pub fn take(from set: Set(member), keeping desired: List(member)) -> Set(member) { - Set(map.take(from: set.map, keeping: desired)) + Set(dict.take(from: set.map, keeping: desired)) } fn order(first: Set(member), second: Set(member)) -> #(Set(member), Set(member)) { - case map.size(first.map) > map.size(second.map) { + case dict.size(first.map) > dict.size(second.map) { True -> #(first, second) False -> #(second, first) } diff --git a/src/gleam_stdlib.erl b/src/gleam_stdlib.erl index 7414439..c6ea125 100644 --- a/src/gleam_stdlib.erl +++ b/src/gleam_stdlib.erl @@ -378,7 +378,7 @@ inspect(Data) when is_map(Data) -> [<<"#(">>, inspect(Key), <<", ">>, inspect(Value), <<")">>] || {Key, Value} <- maps:to_list(Data) ], - ["map.from_list([", lists:join(", ", Fields), "])"]; + ["dict.from_list([", lists:join(", ", Fields), "])"]; inspect(Atom) when is_atom(Atom) -> Binary = erlang:atom_to_binary(Atom), case inspect_maybe_gleam_atom(Binary, none, <<>>) of |