diff options
-rw-r--r-- | src/gleam/list.gleam | 81 | ||||
-rw-r--r-- | src/gleam/map.gleam | 144 | ||||
-rw-r--r-- | test/gleam/map_test.gleam | 1 |
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), |