aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Pilfold <louis@lpil.uk>2023-11-21 14:39:00 +0000
committerLouis Pilfold <louis@lpil.uk>2023-11-21 15:11:37 +0000
commit6139295179325810d53613879613b4181188d56a (patch)
tree819b7bd4e26a50d5e45dc5d6e638078d8d56c4cf
parentaec149b434c1ca537c2fe9d307113c226413e035 (diff)
downloadgleam_stdlib-6139295179325810d53613879613b4181188d56a.tar.gz
gleam_stdlib-6139295179325810d53613879613b4181188d56a.zip
map -> dict
-rw-r--r--src/gleam/dict.gleam548
-rw-r--r--src/gleam/dynamic.gleam34
-rw-r--r--src/gleam/iterator.gleam16
-rw-r--r--src/gleam/list.gleam18
-rw-r--r--src/gleam/map.gleam532
-rw-r--r--src/gleam/set.gleam28
-rw-r--r--src/gleam_stdlib.erl2
-rw-r--r--test/gleam/dict_test.gleam393
-rw-r--r--test/gleam/dynamic_test.gleam70
-rw-r--r--test/gleam/iterator_test.gleam4
-rw-r--r--test/gleam/list_test.gleam16
-rw-r--r--test/gleam/string_test.gleam6
12 files changed, 1095 insertions, 572 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
diff --git a/test/gleam/dict_test.gleam b/test/gleam/dict_test.gleam
new file mode 100644
index 0000000..9ab074b
--- /dev/null
+++ b/test/gleam/dict_test.gleam
@@ -0,0 +1,393 @@
+import gleam/dict
+import gleam/option.{None, Some}
+import gleam/should
+import gleam/string
+import gleam/list
+import gleam/int
+
+pub fn from_list_test() {
+ [#(4, 0), #(1, 0)]
+ |> dict.from_list
+ |> dict.size
+ |> should.equal(2)
+
+ [#(1, 0), #(1, 1)]
+ |> dict.from_list
+ |> should.equal(dict.from_list([#(1, 1)]))
+}
+
+pub fn has_key_test() {
+ []
+ |> dict.from_list
+ |> dict.has_key(1)
+ |> should.be_false
+
+ [#(1, 0)]
+ |> dict.from_list
+ |> dict.has_key(1)
+ |> should.be_true
+
+ [#(4, 0), #(1, 0)]
+ |> dict.from_list
+ |> dict.has_key(1)
+ |> should.be_true
+
+ [#(4, 0), #(1, 0)]
+ |> dict.from_list
+ |> dict.has_key(0)
+ |> should.be_false
+}
+
+pub fn new_test() {
+ dict.new()
+ |> dict.size
+ |> should.equal(0)
+
+ dict.new()
+ |> dict.to_list
+ |> should.equal([])
+}
+
+type Key {
+ A
+ B
+ C
+}
+
+pub fn get_test() {
+ let proplist = [#(4, 0), #(1, 1)]
+ let m = dict.from_list(proplist)
+
+ m
+ |> dict.get(4)
+ |> should.equal(Ok(0))
+
+ m
+ |> dict.get(1)
+ |> should.equal(Ok(1))
+
+ m
+ |> dict.get(2)
+ |> should.equal(Error(Nil))
+
+ let proplist = [#(A, 0), #(B, 1)]
+ let m = dict.from_list(proplist)
+
+ m
+ |> dict.get(A)
+ |> should.equal(Ok(0))
+
+ m
+ |> dict.get(B)
+ |> should.equal(Ok(1))
+
+ m
+ |> dict.get(C)
+ |> should.equal(Error(Nil))
+
+ let proplist = [#(<<1, 2, 3>>, 0), #(<<3, 2, 1>>, 1)]
+ let m = dict.from_list(proplist)
+
+ m
+ |> dict.get(<<1, 2, 3>>)
+ |> should.equal(Ok(0))
+
+ m
+ |> dict.get(<<3, 2, 1>>)
+ |> should.equal(Ok(1))
+
+ m
+ |> dict.get(<<1, 3, 2>>)
+ |> should.equal(Error(Nil))
+}
+
+pub fn insert_test() {
+ dict.new()
+ |> dict.insert("a", 0)
+ |> dict.insert("b", 1)
+ |> dict.insert("c", 2)
+ |> should.equal(dict.from_list([#("a", 0), #("b", 1), #("c", 2)]))
+}
+
+pub fn map_values_test() {
+ [#(1, 0), #(2, 1), #(3, 2)]
+ |> dict.from_list
+ |> dict.map_values(fn(k, v) { k + v })
+ |> should.equal(dict.from_list([#(1, 1), #(2, 3), #(3, 5)]))
+}
+
+pub fn keys_test() {
+ [#("a", 0), #("b", 1), #("c", 2)]
+ |> dict.from_list
+ |> dict.keys
+ |> list.sort(string.compare)
+ |> should.equal(["a", "b", "c"])
+}
+
+pub fn values_test() {
+ [#("a", 0), #("b", 1), #("c", 2)]
+ |> dict.from_list
+ |> dict.values
+ |> list.sort(int.compare)
+ |> should.equal([0, 1, 2])
+}
+
+pub fn take_test() {
+ [#("a", 0), #("b", 1), #("c", 2)]
+ |> dict.from_list
+ |> dict.take(["a", "b", "d"])
+ |> should.equal(dict.from_list([#("a", 0), #("b", 1)]))
+}
+
+pub fn drop_test() {
+ [#("a", 0), #("b", 1), #("c", 2)]
+ |> dict.from_list
+ |> dict.drop(["a", "b", "d"])
+ |> should.equal(dict.from_list([#("c", 2)]))
+}
+
+pub fn merge_same_key_test() {
+ let a = dict.from_list([#("a", 2)])
+ let b = dict.from_list([#("a", 0)])
+
+ dict.merge(a, b)
+ |> should.equal(dict.from_list([#("a", 0)]))
+
+ dict.merge(b, a)
+ |> should.equal(dict.from_list([#("a", 2)]))
+}
+
+pub fn merge_test() {
+ let a = dict.from_list([#("a", 2), #("c", 4), #("d", 3)])
+ let b = dict.from_list([#("a", 0), #("b", 1), #("c", 2)])
+
+ dict.merge(a, b)
+ |> should.equal(dict.from_list([#("a", 0), #("b", 1), #("c", 2), #("d", 3)]))
+
+ dict.merge(b, a)
+ |> should.equal(dict.from_list([#("a", 2), #("b", 1), #("c", 4), #("d", 3)]))
+}
+
+pub fn delete_test() {
+ [#("a", 0), #("b", 1), #("c", 2)]
+ |> dict.from_list
+ |> dict.delete("a")
+ |> dict.delete("d")
+ |> should.equal(dict.from_list([#("b", 1), #("c", 2)]))
+}
+
+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) -> i + 1
+ None -> 0
+ }
+ }
+
+ dict
+ |> dict.update("a", inc_or_zero)
+ |> should.equal(dict.from_list([#("a", 1), #("b", 1), #("c", 2)]))
+
+ dict
+ |> dict.update("b", inc_or_zero)
+ |> should.equal(dict.from_list([#("a", 0), #("b", 2), #("c", 2)]))
+
+ dict
+ |> dict.update("z", inc_or_zero)
+ |> should.equal(dict.from_list([#("a", 0), #("b", 1), #("c", 2), #("z", 0)]))
+}
+
+pub fn fold_test() {
+ let dict = dict.from_list([#("a", 0), #("b", 1), #("c", 2), #("d", 3)])
+
+ let add = fn(acc, _, v) { v + acc }
+
+ dict
+ |> dict.fold(0, add)
+ |> should.equal(6)
+
+ let prepend = fn(acc, k, _) { list.prepend(acc, k) }
+
+ dict
+ |> dict.fold([], prepend)
+ |> list.sort(string.compare)
+ |> should.equal(["a", "b", "c", "d"])
+
+ dict.from_list([])
+ |> dict.fold(0, add)
+ |> should.equal(0)
+}
+
+fn range(start, end, a) {
+ case end - start {
+ n if n < 1 -> a
+ _ -> range(start, end - 1, [end - 1, ..a])
+ }
+}
+
+fn list_to_map(list) {
+ list
+ |> list.map(fn(n) { #(n, n) })
+ |> dict.from_list
+}
+
+fn grow_and_shrink_map(initial_size, final_size) {
+ range(0, initial_size, [])
+ |> list_to_map
+ |> list.fold(
+ range(final_size, initial_size, []),
+ _,
+ fn(map, item) { dict.delete(map, item) },
+ )
+}
+
+// maps should be equal even if the insert/removal order was different
+pub fn insert_order_equality_test() {
+ grow_and_shrink_map(8, 2)
+ |> should.equal(grow_and_shrink_map(4, 2))
+ grow_and_shrink_map(17, 10)
+ |> should.equal(grow_and_shrink_map(12, 10))
+ grow_and_shrink_map(2000, 1000)
+ |> should.equal(grow_and_shrink_map(1000, 1000))
+}
+
+// ensure operations on a map don't mutate it
+pub fn persistence_test() {
+ let a = list_to_map([0])
+ dict.insert(a, 0, 5)
+ dict.insert(a, 1, 6)
+ dict.delete(a, 0)
+ dict.get(a, 0)
+ |> should.equal(Ok(0))
+}
+
+// using maps as keys should work (tests hash function)
+pub fn map_as_key_test() {
+ let l = range(0, 1000, [])
+ let a = list_to_map(l)
+ let a2 = list_to_map(list.reverse(l))
+ let a3 = grow_and_shrink_map(2000, 1000)
+ let b = grow_and_shrink_map(60, 50)
+ let c = grow_and_shrink_map(50, 20)
+ let d = grow_and_shrink_map(2, 2)
+
+ let map1 =
+ dict.new()
+ |> dict.insert(a, "a")
+ |> dict.insert(b, "b")
+ |> dict.insert(c, "c")
+ |> dict.insert(d, "d")
+
+ dict.get(map1, a)
+ |> should.equal(Ok("a"))
+ dict.get(map1, a2)
+ |> should.equal(Ok("a"))
+ dict.get(map1, a3)
+ |> should.equal(Ok("a"))
+ dict.get(map1, b)
+ |> should.equal(Ok("b"))
+ dict.get(map1, c)
+ |> should.equal(Ok("c"))
+ dict.get(map1, d)
+ |> should.equal(Ok("d"))
+ dict.insert(map1, a2, "a2")
+ |> dict.get(a)
+ |> should.equal(Ok("a2"))
+ dict.insert(map1, a3, "a3")
+ |> dict.get(a)
+ |> should.equal(Ok("a3"))
+}
+
+pub fn large_n_test() {
+ let n = 10_000
+ let l = range(0, n, [])
+
+ let m = list_to_map(l)
+ list.map(l, fn(i) { should.equal(dict.get(m, i), Ok(i)) })
+
+ let m = grow_and_shrink_map(n, 0)
+ list.map(l, fn(i) { should.equal(dict.get(m, i), Error(Nil)) })
+}
+
+pub fn size_test() {
+ let n = 1000
+ let m = list_to_map(range(0, n, []))
+ dict.size(m)
+ |> should.equal(n)
+
+ let m = grow_and_shrink_map(n, n / 2)
+ dict.size(m)
+ |> should.equal(n / 2)
+
+ let m =
+ grow_and_shrink_map(n, 0)
+ |> dict.delete(0)
+ dict.size(m)
+ |> should.equal(0)
+
+ let m = list_to_map(range(0, 18, []))
+
+ dict.insert(m, 1, 99)
+ |> dict.size()
+ |> should.equal(18)
+ dict.insert(m, 2, 99)
+ |> dict.size()
+ |> should.equal(18)
+}
+
+// https://github.com/gleam-lang/stdlib/issues/435
+pub fn peters_bug_test() {
+ dict.new()
+ |> dict.insert(22, Nil)
+ |> dict.insert(21, Nil)
+ |> dict.insert(23, Nil)
+ |> dict.insert(18, Nil)
+ |> dict.insert(17, Nil)
+ |> dict.insert(19, Nil)
+ |> dict.insert(14, Nil)
+ |> dict.insert(13, Nil)
+ |> dict.insert(15, Nil)
+ |> dict.insert(10, Nil)
+ |> dict.insert(9, Nil)
+ |> dict.insert(11, Nil)
+ |> dict.insert(6, Nil)
+ |> dict.insert(5, Nil)
+ |> dict.insert(7, Nil)
+ |> dict.insert(2, Nil)
+ |> dict.insert(1, Nil)
+ |> dict.insert(3, Nil)
+ |> dict.get(0)
+ |> should.equal(Error(Nil))
+}
+
+pub fn zero_must_be_contained_test() {
+ let map =
+ dict.new()
+ |> dict.insert(0, Nil)
+
+ map
+ |> dict.get(0)
+ |> should.equal(Ok(Nil))
+
+ map
+ |> dict.has_key(0)
+ |> should.equal(True)
+}
+
+pub fn empty_map_equality_test() {
+ let map1 = dict.new()
+ let map2 = dict.from_list([#(1, 2)])
+
+ should.be_false(map1 == map2)
+ should.be_false(map2 == map1)
+}
+
+pub fn extra_keys_equality_test() {
+ let map1 = dict.from_list([#(1, 2), #(3, 4)])
+ let map2 = dict.from_list([#(1, 2), #(3, 4), #(4, 5)])
+
+ should.be_false(map1 == map2)
+ should.be_false(map2 == map1)
+}
diff --git a/test/gleam/dynamic_test.gleam b/test/gleam/dynamic_test.gleam
index 06941f3..c072756 100644
--- a/test/gleam/dynamic_test.gleam
+++ b/test/gleam/dynamic_test.gleam
@@ -1,5 +1,5 @@
import gleam/dynamic.{DecodeError}
-import gleam/map
+import gleam/dict
import gleam/option.{None, Some}
import gleam/result
import gleam/should
@@ -293,34 +293,34 @@ pub fn javascript_object_field_test() {
}
pub fn field_test() {
- map.new()
- |> map.insert("ok", 1)
+ dict.new()
+ |> dict.insert("ok", 1)
|> dynamic.from
|> dynamic.field(named: "ok", of: dynamic.int)
|> should.equal(Ok(1))
- map.new()
- |> map.insert("ok", 1.0)
+ dict.new()
+ |> dict.insert("ok", 1.0)
|> dynamic.from
|> dynamic.field(named: "ok", of: dynamic.float)
|> should.equal(Ok(1.0))
- map.new()
- |> map.insert("ok", 3)
- |> map.insert("error", 1)
+ dict.new()
+ |> dict.insert("ok", 3)
+ |> dict.insert("error", 1)
|> dynamic.from
|> dynamic.field("ok", dynamic.int)
|> should.equal(Ok(3))
- map.new()
- |> map.insert("ok", 3)
+ dict.new()
+ |> dict.insert("ok", 3)
|> dynamic.from
|> dynamic.field("ok", dynamic.string)
|> should.equal(Error([
DecodeError(expected: "String", found: "Int", path: ["ok"]),
]))
- map.new()
+ dict.new()
|> dynamic.from
|> dynamic.field("ok", dynamic.int)
|> should.equal(Error([
@@ -337,8 +337,8 @@ pub fn field_test() {
|> dynamic.field("ok", dynamic.int)
|> should.equal(Error([DecodeError(expected: "Map", found: "List", path: [])]))
- map.new()
- |> map.insert("ok", 1)
+ dict.new()
+ |> dict.insert("ok", 1)
|> dynamic.from
|> dynamic.field("ok", dynamic.field("not_a_field", dynamic.int))
|> should.equal(Error([
@@ -347,46 +347,46 @@ pub fn field_test() {
}
pub fn optional_field_test() {
- map.new()
- |> map.insert("ok", 1)
+ dict.new()
+ |> dict.insert("ok", 1)
|> dynamic.from
|> dynamic.optional_field(named: "ok", of: dynamic.int)
|> should.equal(Ok(Some(1)))
- map.new()
- |> map.insert("ok", 1.0)
+ dict.new()
+ |> dict.insert("ok", 1.0)
|> dynamic.from
|> dynamic.optional_field(named: "ok", of: dynamic.float)
|> should.equal(Ok(Some(1.0)))
- map.new()
- |> map.insert("ok", 3)
- |> map.insert("error", 1)
+ dict.new()
+ |> dict.insert("ok", 3)
+ |> dict.insert("error", 1)
|> dynamic.from
|> dynamic.optional_field("ok", dynamic.int)
|> should.equal(Ok(Some(3)))
- map.new()
- |> map.insert("ok", 3)
+ dict.new()
+ |> dict.insert("ok", 3)
|> dynamic.from
|> dynamic.optional_field("ok", dynamic.string)
|> should.equal(Error([
DecodeError(expected: "String", found: "Int", path: ["ok"]),
]))
- map.new()
- |> map.insert("ok", None)
+ dict.new()
+ |> dict.insert("ok", None)
|> dynamic.from
|> dynamic.optional_field("ok", dynamic.int)
|> should.equal(Ok(None))
- map.new()
- |> map.insert("ok", Nil)
+ dict.new()
+ |> dict.insert("ok", Nil)
|> dynamic.from
|> dynamic.optional_field("ok", dynamic.int)
|> should.equal(Ok(None))
- map.new()
+ dict.new()
|> dynamic.from
|> dynamic.optional_field("ok", dynamic.int)
|> should.equal(Ok(None))
@@ -459,8 +459,8 @@ pub fn element_test() {
|> dynamic.element(0, dynamic.int)
|> should.equal(Error([DecodeError(expected: "Tuple", found: "Int", path: [])]))
- map.new()
- |> map.insert(1, "ok")
+ dict.new()
+ |> dict.insert(1, "ok")
|> dynamic.from
|> dynamic.element(0, dynamic.int)
|> should.equal(Error([DecodeError(expected: "Tuple", found: "Map", path: [])]))
@@ -999,24 +999,24 @@ pub fn nested_tuples_test() {
}
pub fn map_test() {
- map.new()
+ dict.new()
|> dynamic.from
|> dynamic.map(dynamic.string, dynamic.int)
- |> should.equal(Ok(map.new()))
+ |> should.equal(Ok(dict.new()))
- map.from_list([#("a", 1), #("b", 2)])
+ dict.from_list([#("a", 1), #("b", 2)])
|> dynamic.from
|> dynamic.map(dynamic.string, dynamic.int)
- |> should.equal(Ok(map.from_list([#("a", 1), #("b", 2)])))
+ |> should.equal(Ok(dict.from_list([#("a", 1), #("b", 2)])))
- map.from_list([#("a", 1), #("b", 2)])
+ dict.from_list([#("a", 1), #("b", 2)])
|> dynamic.from
|> dynamic.map(dynamic.int, dynamic.int)
|> should.equal(Error([
DecodeError(expected: "Int", found: "String", path: ["keys"]),
]))
- map.from_list([#("a", 1), #("b", 2)])
+ dict.from_list([#("a", 1), #("b", 2)])
|> dynamic.from
|> dynamic.map(dynamic.string, dynamic.string)
|> should.equal(Error([
diff --git a/test/gleam/iterator_test.gleam b/test/gleam/iterator_test.gleam
index adb4802..a83decc 100644
--- a/test/gleam/iterator_test.gleam
+++ b/test/gleam/iterator_test.gleam
@@ -1,6 +1,6 @@
import gleam/iterator.{Done, Next}
import gleam/list
-import gleam/map
+import gleam/dict
import gleam/should
// a |> from_list |> to_list == a
@@ -480,7 +480,7 @@ pub fn all_test() {
pub fn group_test() {
iterator.from_list([1, 2, 3, 4, 5, 6])
|> iterator.group(by: fn(n) { n % 3 })
- |> should.equal(map.from_list([#(0, [3, 6]), #(1, [1, 4]), #(2, [2, 5])]))
+ |> should.equal(dict.from_list([#(0, [3, 6]), #(1, [1, 4]), #(2, [2, 5])]))
}
pub fn reduce_test() {
diff --git a/test/gleam/list_test.gleam b/test/gleam/list_test.gleam
index ae066ad..0bebe2f 100644
--- a/test/gleam/list_test.gleam
+++ b/test/gleam/list_test.gleam
@@ -2,7 +2,7 @@ import gleam/float
import gleam/pair
import gleam/int
import gleam/list
-import gleam/map
+import gleam/dict
import gleam/should
@target(erlang)
@@ -103,17 +103,17 @@ pub fn group_test() {
}
})
|> should.equal(
- map.new()
- |> map.insert("Failed", [Error("Wrong")])
- |> map.insert("Successful", [Ok(1), Ok(200), Ok(10)]),
+ dict.new()
+ |> dict.insert("Failed", [Error("Wrong")])
+ |> dict.insert("Successful", [Ok(1), Ok(200), Ok(10)]),
)
list.group([1, 2, 3, 4, 5], fn(i) { i - i / 3 * 3 })
|> should.equal(
- map.new()
- |> map.insert(0, [3])
- |> map.insert(1, [4, 1])
- |> map.insert(2, [5, 2]),
+ dict.new()
+ |> dict.insert(0, [3])
+ |> dict.insert(1, [4, 1])
+ |> dict.insert(2, [5, 2]),
)
}
diff --git a/test/gleam/string_test.gleam b/test/gleam/string_test.gleam
index 19341d0..bb3f502 100644
--- a/test/gleam/string_test.gleam
+++ b/test/gleam/string_test.gleam
@@ -1,5 +1,5 @@
import gleam/option.{None, Some}
-import gleam/map
+import gleam/dict
import gleam/order
import gleam/should
import gleam/string
@@ -1089,7 +1089,7 @@ pub fn byte_size_test() {
}
pub fn inspect_map_test() {
- map.from_list([#("a", 1), #("b", 2)])
+ dict.from_list([#("a", 1), #("b", 2)])
|> string.inspect
- |> should.equal("map.from_list([#(\"a\", 1), #(\"b\", 2)])")
+ |> should.equal("dict.from_list([#(\"a\", 1), #(\"b\", 2)])")
}