aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/gleam/list.gleam81
-rw-r--r--src/gleam/map.gleam144
-rw-r--r--test/gleam/map_test.gleam1
3 files changed, 195 insertions, 31 deletions
diff --git a/src/gleam/list.gleam b/src/gleam/list.gleam
index a721294..67bd778 100644
--- a/src/gleam/list.gleam
+++ b/src/gleam/list.gleam
@@ -51,7 +51,6 @@ pub type LengthMismatch {
/// > length([1, 2])
/// 2
///
-///
pub external fn length(of: List(a)) -> Int = "erlang" "length"
/// Create a new list from a given list containing the same elements but in the
@@ -74,7 +73,6 @@ pub external fn length(of: List(a)) -> Int = "erlang" "length"
/// > reverse([1, 2])
/// [2, 1]
///
-///
pub external fn reverse(List(a)) -> List(a) = "lists" "reverse"
/// Determine whether or not the list is empty.
@@ -92,7 +90,6 @@ pub external fn reverse(List(a)) -> List(a) = "lists" "reverse"
/// > is_empty([1, 1])
/// False
///
-///
pub fn is_empty(list: List(a)) -> Bool {
list == []
}
@@ -119,7 +116,6 @@ pub fn is_empty(list: List(a)) -> Bool {
/// > contains([1, 0], 0)
/// True
///
-///
pub fn contains(list: List(a), has elem: a) -> Bool {
case list {
[] -> False
@@ -140,7 +136,6 @@ pub fn contains(list: List(a), has elem: a) -> Bool {
/// > head([1, 2])
/// Ok(1)
///
-///
pub fn head(list: List(a)) -> Option(a) {
case list {
[] -> result.none()
@@ -164,7 +159,6 @@ pub fn head(list: List(a)) -> Option(a) {
/// > tail([1, 2])
/// Ok([2])
///
-///
pub fn tail(list: List(a)) -> Option(List(a)) {
case list {
[] -> result.none()
@@ -196,7 +190,6 @@ fn do_filter(list: List(a), fun: fn(a) -> Bool, acc: List(a)) -> List(a) {
/// > filter([2, 4, 6, 1], fn(x) { x > 6 })
/// []
///
-///
pub fn filter(list: List(a), for predicate: fn(a) -> Bool) -> List(a) {
do_filter(list, predicate, [])
}
@@ -216,7 +209,6 @@ fn do_map(list: List(a), fun: fn(a) -> b, acc: List(b)) -> List(b) {
/// > map([2, 4, 6], fn(x) { x * 2 })
/// [4, 8, 12]
///
-///
pub fn map(list: List(a), with fun: fn(a) -> b) -> List(b) {
do_map(list, fun, [])
}
@@ -244,7 +236,6 @@ fn do_index_map(
/// > index_map(["a", "b"], fn(i, x) { tuple(i, x) })
/// [tuple(0, "a"), tuple(1, "b")]
///
-///
pub fn index_map(list: List(a), with fun: fn(Int, a) -> b) -> List(b) {
do_index_map(list, fun, 0, [])
}
@@ -289,7 +280,6 @@ fn do_traverse(
/// > traverse([[1], [], [2]], head)
/// Error(Nil)
///
-///
pub fn traverse(
list: List(a),
with fun: fn(a) -> Result(b, e),
@@ -313,7 +303,6 @@ pub fn traverse(
/// > drop([1, 2, 3, 4], 9)
/// []
///
-///
pub fn drop(from list: List(a), up_to n: Int) -> List(a) {
case n <= 0 {
True -> list
@@ -352,7 +341,6 @@ fn do_take(list: List(a), n: Int, acc: List(a)) -> List(a) {
/// > take([1, 2, 3, 4], 9)
/// [1, 2, 3, 4]
///
-///
pub fn take(from list: List(a), up_to n: Int) -> List(a) {
do_take(list, n, [])
}
@@ -364,7 +352,6 @@ pub fn take(from list: List(a), up_to n: Int) -> List(a) {
/// > new()
/// []
///
-///
pub fn new() -> List(a) {
[]
}
@@ -379,7 +366,6 @@ pub fn new() -> List(a) {
/// > append([1, 2], [3])
/// [1, 2, 3]
///
-///
pub external fn append(List(a), List(a)) -> List(a)
= "lists" "append";
@@ -400,7 +386,6 @@ fn do_flatten(lists: List(List(a)), acc: List(a)) -> List(a) {
/// > flatten([[1], [2, 3], []])
/// [1, 2, 3]
///
-///
pub fn flatten(lists: List(List(a))) -> List(a) {
do_flatten(lists, [])
}
@@ -458,7 +443,6 @@ pub fn fold_right(
/// > find([], fn(x) { True })
/// Error(Nil)
///
-///
pub fn find(
in haystack: List(a),
one_that is_desired: fn(a) -> Bool,
@@ -490,7 +474,6 @@ pub fn find(
/// > find_map([], head)
/// Error(Nil)
///
-///
pub fn find_map(
in haystack: List(a),
with fun: fn(a) -> Option(b),
@@ -520,7 +503,6 @@ pub fn find_map(
/// > all([4, 3], fn(x) { x > 3 })
/// False
///
-///
pub fn all(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool {
case list {
[] -> True
@@ -550,7 +532,6 @@ pub fn all(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool {
/// > any([3, 4], fn(x) { x > 3 })
/// True
///
-///
pub fn any(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool {
case list {
[] -> False
@@ -581,7 +562,6 @@ pub fn any(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool {
/// > zip([1, 2], [3, 4])
/// [tuple(1, 3), tuple(2, 4)]
///
-///
pub fn zip(xs: List(a), ys: List(b)) -> List(tuple(a, b)) {
case xs, ys {
[], _ -> []
@@ -608,7 +588,6 @@ pub fn zip(xs: List(a), ys: List(b)) -> List(tuple(a, b)) {
/// > strict_zip([1, 2], [3, 4])
/// Ok([tuple(1, 3), tuple(2, 4)])
///
-///
pub fn strict_zip(l1: List(a), l2: List(b)) -> Result(List(tuple(a, b)), LengthMismatch) {
case length(of: l1) == length(of: l2) {
True -> Ok(zip(l1, l2))
@@ -628,7 +607,6 @@ pub fn strict_zip(l1: List(a), l2: List(b)) -> Result(List(tuple(a, b)), LengthM
/// > intersperse([], 2)
/// []
///
-///
pub fn intersperse(list: List(a), with elem: a) -> List(a) {
case list {
[] | [_] -> list
@@ -649,7 +627,6 @@ pub fn intersperse(list: List(a), with elem: a) -> List(a) {
/// > at([1, 2, 3], 5)
/// Error(Nil)
///
-///
pub fn at(in list: List(a), get index: Int) -> Option(a) {
case index < 0 {
True -> result.none()
@@ -674,7 +651,6 @@ pub fn at(in list: List(a), get index: Int) -> Option(a) {
/// > unique([1, 1, 1, 4, 7, 3, 3, 4])
/// [1, 4, 7, 3]
///
-///
pub fn unique(list: List(a)) -> List(a) {
case list {
[] -> []
@@ -713,6 +689,12 @@ fn do_sort(list: List(a), compare: fn(a, a) -> Order, list_length: Int) -> List(
/// Sort from smallest to largest based upon the ordering specified by a given
/// function.
///
+/// ## Examples
+///
+/// > import gleam/int
+/// > list.sort([4, 3, 6, 5, 4, 1, 2], int.compare)
+/// [1, 2, 3, 4, 4, 5, 6]
+///
pub fn sort(list: List(a), sort_by compare: fn(a, a) -> Order) -> List(a) {
do_sort(list, compare, length(list))
}
@@ -730,7 +712,6 @@ pub fn sort(list: List(a), sort_by compare: fn(a, a) -> Order) -> List(a) {
/// > range(1, -5)
/// [1, 0, -1, -2, -3, -4]
///
-///
pub fn range(from start: Int, to stop: Int) -> List(Int) {
case int.compare(start, stop) {
order.Eq -> []
@@ -771,6 +752,22 @@ fn do_split(list: List(a), n: Int, taken: List(a)) -> tuple(List(a), List(a)) {
}
}
+/// Split a list in two before the given index.
+///
+/// If the list is not long enough to have the given index the before list will
+/// be the input list, and the after list will be empty.
+///
+/// ## Examples
+///
+/// > split([6, 7, 8, 9], 0)
+/// tuple([], [6, 7, 8, 9])
+///
+/// > split([6, 7, 8, 9], 2)
+/// tuple([6, 7], [8, 9])
+///
+/// > split([6, 7, 8, 9], 4)
+/// tuple([6, 7, 8, 9], [])
+///
pub fn split(list list: List(a), at index: Int) -> tuple(List(a), List(a)) {
do_split(list, index, [])
}
@@ -790,6 +787,20 @@ fn do_split_while(
}
}
+/// Split a list in two before the first element that a given function returns
+/// False for.
+///
+/// If the function returns True for all elements the first list will be the
+/// input list, and the second list will be empty.
+///
+/// ## Examples
+///
+/// > split_while([1, 2, 3, 4, 5], fn(x) { x <= 3 })
+/// tuple([1, 2, 3], [4, 5])
+///
+/// > split_while([1, 2, 3, 4, 5], fn(x) { x <= 5 })
+/// tuple([1, 2, 3, 4, 5], [])
+///
pub fn split_while(
list list: List(a),
while predicate: fn(a) -> Bool,
@@ -797,6 +808,26 @@ pub fn split_while(
do_split_while(list, predicate, [])
}
+
+/// Given a list of 2 element tuples, find the first tuple that has a given
+/// key as the first element and return the second element.
+///
+/// If no tuple is found with the given key then `Error(Nil)` is returned.
+///
+/// This function may be useful for interacting with Erlang code where lists of
+/// tuples are common.
+///
+/// ## Examples
+///
+/// > key_find([tuple("a", 0), tuple("b", 1)], "a")
+/// Ok(0)
+///
+/// > key_find([tuple("a", 0), tuple("b", 1)], "b")
+/// Ok(1)
+///
+/// > key_find([tuple("a", 0), tuple("b", 1)], "c")
+/// Error(Nil)
+///
pub fn key_find(
in keyword_list: List(tuple(k, v)),
find desired_key: k,
diff --git a/src/gleam/map.gleam b/src/gleam/map.gleam
index fd49433..daaee6e 100644
--- a/src/gleam/map.gleam
+++ b/src/gleam/map.gleam
@@ -10,7 +10,8 @@ import gleam/result.{Option}
/// 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.
+/// 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.
@@ -45,7 +46,6 @@ pub external fn size(Map(k, v)) -> Int
/// > new() |> insert("key", 0) |> to_list()
/// [tuple("key", 0)]
///
-///
pub external fn to_list(Map(key, value)) -> List(tuple(key, value))
= "maps" "to_list"
@@ -70,13 +70,12 @@ external fn is_key(key, Map(key, v)) -> Bool
/// > new() |> insert("a", 0) |> has_key("b")
/// False
///
-///
pub fn has_key(map: Map(k, v), key: k) -> Bool {
is_key(key, map)
}
-/// Create a new map that contains no values.
+/// Create a fresh map that contains no values.
///
pub external fn new() -> Map(key, value)
= "maps" "new"
@@ -94,7 +93,6 @@ pub external fn new() -> Map(key, value)
/// > new() |> insert("a", 0) |> get("b")
/// Error(Nil)
///
-///
pub external fn get(from: Map(key, value), get: key) -> Option(value)
= "gleam_stdlib" "map_get";
@@ -114,7 +112,6 @@ external fn erl_insert(key, value, Map(key, value)) -> Map(key, value)
/// > new() |> insert("a", 0) |> insert("a", 5) |> to_list
/// [tuple("a", 5)]
///
-///
pub fn insert(into map: Map(k, v), for key: k, insert value: v) -> Map(k, v) {
erl_insert(key, value, map)
}
@@ -123,13 +120,46 @@ external fn erl_map_values(fn(key, a) -> b, Map(key, value))
-> Map(key, b)
= "maps" "map";
+/// Update all values in a given map by calling a given function on each key
+/// and value.
+///
+/// ## Examples
+///
+/// > [tuple(3, 3), tuple(2, 4)]
+/// > |> from_list
+/// > |> map_values(fn(key, value) { key * value })
+/// [tuple(3, 9), tuple(2, 8)]
+///
+///
pub fn map_values(in map: Map(k, v), with fun: fn(k, v) -> w) -> Map(k, w) {
erl_map_values(fun, map)
}
+/// Get 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
+///
+/// > keys([tuple("a", 0), tuple("b", 1)])
+/// ["a", "b"]
+///
pub external fn keys(Map(keys, v)) -> List(keys)
= "maps" "keys"
+/// Get 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
+///
+/// > keys(from_list([tuple("a", 0), tuple("b", 1)]))
+/// [0, 1]
+///
pub external fn values(Map(k, values)) -> List(values)
= "maps" "values"
@@ -137,6 +167,19 @@ external fn erl_filter(fn(key, value) -> Bool, Map(key, value))
-> Map(key, value)
= "maps" "filter";
+/// Create a new map from a given map, minus any entries that a given function
+/// returns False for.
+///
+/// ## Examples
+///
+/// > from_list([tuple("a", 0), tuple("b", 1)])
+/// > |> filter(fn(key, value) { value != 0 })
+/// from_list([tuple("b", 1)])
+///
+/// > from_list([tuple("a", 0), tuple("b", 1)])
+/// > |> filter(fn(key, value) { True })
+/// from_list([tuple("a", 0), tuple("b", 1)])
+///
pub fn filter(in map: Map(k, v), for predicate: fn(k, v) -> Bool) -> Map(k, v) {
erl_filter(predicate, map)
}
@@ -144,26 +187,98 @@ pub fn filter(in map: Map(k, v), for predicate: fn(k, v) -> Bool) -> Map(k, v) {
external fn erl_take(List(k), Map(k, v)) -> Map(k, v)
= "maps" "with"
+/// Create a new map from a given map, only including any entries for which the
+/// keys are in a given list.
+///
+/// ## Examples
+///
+/// > from_list([tuple("a", 0), tuple("b", 1)])
+/// > |> take(["b"])
+/// from_list([tuple("b", 1)])
+///
+/// > from_list([tuple("a", 0), tuple("b", 1)])
+/// > |> take(["a", "b", "c"])
+/// from_list([tuple("a", 0), tuple("b", 1)])
+///
pub fn take(from map: Map(k, v), drop desired_keys: List(k)) -> Map(k, v) {
erl_take(desired_keys, map)
}
+/// Create 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
+///
+/// > let a = from_list([tuple("a", 0), tuple("b", 1)])
+/// > let b = from_list([tuple("b", 2), tuple("c", 3)])
+/// > merge(a, b)
+/// from_list([tuple("a", 0), tuple("b", 2), tuple("c", 3)])
+///
pub external fn merge(into: Map(k, v), merge: Map(k, v)) -> Map(k, v)
= "maps" "merge"
external fn erl_delete(k, Map(k, v)) -> Map(k, v)
= "maps" "remove"
+
+/// Create a new map from a given map with all the same entries except for the
+/// one with a given key, if it exists.
+///
+/// ## Examples
+///
+/// > delete([tuple("a", 0), tuple("b", 1)], "a")
+/// from_list([tuple("b", 2)])
+///
+/// > delete([tuple("a", 0), tuple("b", 1)], "c")
+/// from_list([tple("a", 0), tuple("b", 2)])
+///
pub fn delete(from map: Map(k, v), delete key: k) -> Map(k, v) {
erl_delete(key, map)
}
+/// Create a new map from a given map with all the same entries except any with
+/// keys found in a given list.
+///
+/// ## Examples
+///
+/// > drop([tuple("a", 0), tuple("b", 1)], ["a"])
+/// from_list([tuple("b", 2)])
+///
+/// > delete([tuple("a", 0), tuple("b", 1)], ["c"])
+/// from_list([tple("a", 0), tuple("b", 2)])
+///
+/// > drop([tuple("a", 0), tuple("b", 1)], ["a", "b", "c"])
+/// from_list([])
+///
pub fn drop(from map: Map(k, v), drop disallowed_keys: List(k)) -> Map(k, v) {
list.fold(disallowed_keys, map, fn(key, acc) {
delete(acc, key)
})
}
+/// Create 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 `Error(Nil)` as its argument, otherwise it gets `Ok(value)`.
+///
+/// ## Example
+///
+/// > let increment = fn(x) {
+/// > case x {
+/// > Ok(i) -> i + 1
+/// > Error(Nil) -> 0
+/// > }
+/// > }
+/// > let map = from_list([tuple("a", 0)])
+/// >
+/// > update(map, "a" increment)
+/// from_list([tuple("a", 1)])
+///
+/// > update(map, "b" increment)
+/// from_list([tuple("a", 0), tuple("b", 0)])
+///
pub fn update(
in map: Map(k, v),
update key: k,
@@ -183,6 +298,23 @@ fn do_fold(
}
}
+/// Combine 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
+///
+/// > let map = from_list([tuple("a", 1), tuple("b", 3), tuple("c", 9)])
+/// > fold(map, 0, fn(key, value, accumulator) { accumulator + value })
+/// 13
+///
+/// > import gleam/string.{append}
+/// > fold(map, "", fn(key, value, accumulator) { append(accumulator, value) })
+/// "abc"
+///
pub fn fold(
map: Map(k, v),
from initial: acc,
diff --git a/test/gleam/map_test.gleam b/test/gleam/map_test.gleam
index 88f2ef3..58b7cb5 100644
--- a/test/gleam/map_test.gleam
+++ b/test/gleam/map_test.gleam
@@ -156,6 +156,7 @@ pub fn merge_test() {
tuple("c", 4),
tuple("d", 3),
])
+
let b = map.from_list([
tuple("a", 0),
tuple("b", 1),