aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Pilfold <louis@lpil.uk>2021-07-18 17:33:43 +0100
committerLouis Pilfold <louis@lpil.uk>2021-07-18 17:33:43 +0100
commit3d287285eb8a1e824a400d01afa543454eff7b44 (patch)
tree75f0330233b580f12f11b1bd25159e6a2017fa33
parent6b449f10ee19c3012cfb8c33d9c687acd4d0d303 (diff)
downloadgleam_stdlib-3d287285eb8a1e824a400d01afa543454eff7b44.tar.gz
gleam_stdlib-3d287285eb8a1e824a400d01afa543454eff7b44.zip
JS list support
-rw-r--r--CHANGELOG.md3
-rw-r--r--src/gleam/float.gleam197
-rw-r--r--src/gleam/int.gleam234
-rw-r--r--src/gleam/list.gleam2838
-rw-r--r--test/gleam/list_test.gleam1158
5 files changed, 2251 insertions, 2179 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2440c2e..d84fb76 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,8 @@
## Unreleased
- The `os` module has been moved to the `gleam_os` library.
-- The `bool`, `order`, and `pair` modules now support JavaScript compilation.
+- The `bool`, `list`, `order`, and `pair` modules now support JavaScript
+ compilation.
- The `map.update` function now uses `Option` rather than `Result`.
- The `iterator` module gains the `fold_until` function.
diff --git a/src/gleam/float.gleam b/src/gleam/float.gleam
index ed1d3ab..5938be0 100644
--- a/src/gleam/float.gleam
+++ b/src/gleam/float.gleam
@@ -1,6 +1,7 @@
+import gleam/order.{Order}
+
if erlang {
import gleam/string_builder
- import gleam/order.{Order}
pub type Float =
Float
@@ -29,67 +30,69 @@ if erlang {
|> string_builder.from_float
|> string_builder.to_string
}
+}
- /// Restricts a Float between a lower and upper bound
- ///
- /// ## Examples
- ///
- /// ```
- /// > clamp(1.2, min: 1.4, max: 1.6)
- /// 1.4
- /// ```
- ///
- pub fn clamp(n: Float, min min_bound: Float, max max_bound: Float) -> Float {
- n
- |> min(max_bound)
- |> max(min_bound)
- }
+/// Restricts a Float between a lower and upper bound
+///
+/// ## Examples
+///
+/// ```
+/// > clamp(1.2, min: 1.4, max: 1.6)
+/// 1.4
+/// ```
+///
+pub fn clamp(n: Float, min min_bound: Float, max max_bound: Float) -> Float {
+ n
+ |> min(max_bound)
+ |> max(min_bound)
+}
- /// Compares two floats, returning an order.
- ///
- /// ## Examples
- /// > compare(2.0, 2.3)
- /// Lt
- ///
- pub fn compare(a: Float, with b: Float) -> Order {
- case a == b {
- True -> order.Eq
- False ->
- case a <. b {
- True -> order.Lt
- False -> order.Gt
- }
- }
+/// Compares two floats, returning an order.
+///
+/// ## Examples
+/// > compare(2.0, 2.3)
+/// Lt
+///
+pub fn compare(a: Float, with b: Float) -> Order {
+ case a == b {
+ True -> order.Eq
+ False ->
+ case a <. b {
+ True -> order.Lt
+ False -> order.Gt
+ }
}
+}
- /// Compares two floats, returning the smaller of the two.
- ///
- /// ## Examples
- ///
- /// > min(2.0, 2.3)
- /// 2.0
- ///
- pub fn min(a: Float, b: Float) -> Float {
- case a <. b {
- True -> a
- False -> b
- }
+/// Compares two floats, returning the smaller of the two.
+///
+/// ## Examples
+///
+/// > min(2.0, 2.3)
+/// 2.0
+///
+pub fn min(a: Float, b: Float) -> Float {
+ case a <. b {
+ True -> a
+ False -> b
}
+}
- /// Compares two floats, returning the larger of the two.
- ///
- /// ## Examples
- ///
- /// > max(2.0, 2.3)
- /// 2.3
- ///
- pub fn max(a: Float, b: Float) -> Float {
- case a >. b {
- True -> a
- False -> b
- }
+/// Compares two floats, returning the larger of the two.
+///
+/// ## Examples
+///
+/// > max(2.0, 2.3)
+/// 2.3
+///
+pub fn max(a: Float, b: Float) -> Float {
+ case a >. b {
+ True -> a
+ False -> b
}
+}
+if erlang {
/// Rounds the value to the next highest whole number as a float.
///
/// ## Examples
@@ -176,55 +179,55 @@ if erlang {
False -> Ok(power(number, 0.5))
}
}
+}
- /// Returns the negative of the value provided
- ///
- /// ## Examples
- ///
- /// > negate(1.)
- /// -1.
- ///
- pub fn negate(x: Float) -> Float {
- -1. *. x
- }
+/// Returns the negative of the value provided
+///
+/// ## Examples
+///
+/// > negate(1.)
+/// -1.
+///
+pub fn negate(x: Float) -> Float {
+ -1. *. x
+}
- /// Sums a list of Floats.
- ///
- /// ## Example
- ///
- /// > sum([1.0, 2.2, 3.3])
- /// 6.5
- ///
- pub fn sum(numbers: List(Float)) -> Float {
- numbers
- |> do_sum(0.0)
- }
+/// Sums a list of Floats.
+///
+/// ## Example
+///
+/// > sum([1.0, 2.2, 3.3])
+/// 6.5
+///
+pub fn sum(numbers: List(Float)) -> Float {
+ numbers
+ |> do_sum(0.0)
+}
- fn do_sum(numbers: List(Float), initial: Float) -> Float {
- case numbers {
- [] -> initial
- [x, ..rest] -> do_sum(rest, x +. initial)
- }
+fn do_sum(numbers: List(Float), initial: Float) -> Float {
+ case numbers {
+ [] -> initial
+ [x, ..rest] -> do_sum(rest, x +. initial)
}
+}
- /// Multiplies a list of Floats and returns the product.
- ///
- /// ## Example
- ///
- /// > product([2.5, 3.2, 4.2])
- /// 33.6
- ///
- pub fn product(numbers: List(Float)) -> Float {
- case numbers {
- [] -> 0.
- _ -> do_product(numbers, 1.)
- }
+/// Multiplies a list of Floats and returns the product.
+///
+/// ## Example
+///
+/// > product([2.5, 3.2, 4.2])
+/// 33.6
+///
+pub fn product(numbers: List(Float)) -> Float {
+ case numbers {
+ [] -> 0.
+ _ -> do_product(numbers, 1.)
}
+}
- fn do_product(numbers: List(Float), initial: Float) -> Float {
- case numbers {
- [] -> initial
- [x, ..rest] -> do_product(rest, x *. initial)
- }
+fn do_product(numbers: List(Float), initial: Float) -> Float {
+ case numbers {
+ [] -> initial
+ [x, ..rest] -> do_product(rest, x *. initial)
}
}
diff --git a/src/gleam/int.gleam b/src/gleam/int.gleam
index b65d3a3..5c1a588 100644
--- a/src/gleam/int.gleam
+++ b/src/gleam/int.gleam
@@ -1,6 +1,6 @@
-if erlang {
- import gleam/order.{Order}
+import gleam/order.{Order}
+if erlang {
pub type Int =
Int
@@ -90,135 +90,135 @@ if erlang {
|> min(max_bound)
|> max(min_bound)
}
+}
- /// Compares two ints, returning an order.
- ///
- /// ## Examples
- ///
- /// > compare(2, 3)
- /// Lt
- ///
- /// > compare(4, 3)
- /// Gt
- ///
- /// > compare(3, 3)
- /// Eq
- ///
- pub fn compare(a: Int, with b: Int) -> Order {
- case a == b {
- True -> order.Eq
- False ->
- case a < b {
- True -> order.Lt
- False -> order.Gt
- }
- }
+/// Compares two ints, returning an order.
+///
+/// ## Examples
+///
+/// > compare(2, 3)
+/// Lt
+///
+/// > compare(4, 3)
+/// Gt
+///
+/// > compare(3, 3)
+/// Eq
+///
+pub fn compare(a: Int, with b: Int) -> Order {
+ case a == b {
+ True -> order.Eq
+ False ->
+ case a < b {
+ True -> order.Lt
+ False -> order.Gt
+ }
}
+}
- /// Compares two int, returning the smaller of the two.
- ///
- /// ## Examples
- ///
- /// > min(2, 3)
- /// 2
- ///
- pub fn min(a: Int, b: Int) -> Int {
- case a < b {
- True -> a
- False -> b
- }
+/// Compares two int, returning the smaller of the two.
+///
+/// ## Examples
+///
+/// > min(2, 3)
+/// 2
+///
+pub fn min(a: Int, b: Int) -> Int {
+ case a < b {
+ True -> a
+ False -> b
}
+}
- /// Compares two int, returning the larger of the two.
- ///
- /// ## Examples
- ///
- /// > max(2, 3)
- /// 3
- ///
- pub fn max(a: Int, b: Int) -> Int {
- case a > b {
- True -> a
- False -> b
- }
+/// Compares two int, returning the larger of the two.
+///
+/// ## Examples
+///
+/// > max(2, 3)
+/// 3
+///
+pub fn max(a: Int, b: Int) -> Int {
+ case a > b {
+ True -> a
+ False -> b
}
+}
- /// Returns whether the value provided is even.
- ///
- /// ## Examples
- ///
- /// > is_even(2)
- /// True
- ///
- /// > is_even(3)
- /// False
- ///
- pub fn is_even(x: Int) -> Bool {
- x % 2 == 0
- }
+/// Returns whether the value provided is even.
+///
+/// ## Examples
+///
+/// > is_even(2)
+/// True
+///
+/// > is_even(3)
+/// False
+///
+pub fn is_even(x: Int) -> Bool {
+ x % 2 == 0
+}
- /// Returns whether the value provided is odd.
- ///
- /// ## Examples
- ///
- /// > is_odd(3)
- /// True
- ///
- /// > is_odd(2)
- /// False
- ///
- pub fn is_odd(x: Int) -> Bool {
- x % 2 != 0
- }
+/// Returns whether the value provided is odd.
+///
+/// ## Examples
+///
+/// > is_odd(3)
+/// True
+///
+/// > is_odd(2)
+/// False
+///
+pub fn is_odd(x: Int) -> Bool {
+ x % 2 != 0
+}
- /// Returns the negative of the value provided
- ///
- /// ## Examples
- ///
- /// > negate(1)
- /// -1
- ///
- pub fn negate(x: Int) -> Int {
- -1 * x
- }
+/// Returns the negative of the value provided
+///
+/// ## Examples
+///
+/// > negate(1)
+/// -1
+///
+pub fn negate(x: Int) -> Int {
+ -1 * x
+}
- /// Sums a list of Ints.
- ///
- /// ## Example
- ///
- /// > sum([1, 2, 3])
- /// 6
- ///
- pub fn sum(numbers: List(Int)) -> Int {
- numbers
- |> do_sum(0)
- }
+/// Sums a list of Ints.
+///
+/// ## Example
+///
+/// > sum([1, 2, 3])
+/// 6
+///
+pub fn sum(numbers: List(Int)) -> Int {
+ numbers
+ |> do_sum(0)
+}
- fn do_sum(numbers: List(Int), initial: Int) -> Int {
- case numbers {
- [] -> initial
- [x, ..rest] -> do_sum(rest, x + initial)
- }
+fn do_sum(numbers: List(Int), initial: Int) -> Int {
+ case numbers {
+ [] -> initial
+ [x, ..rest] -> do_sum(rest, x + initial)
}
+}
- /// Multiplies a list of Ints and returns the product.
- ///
- /// ## Example
- ///
- /// > product([2, 3, 4])
- /// 24
- ///
- pub fn product(numbers: List(Int)) -> Int {
- case numbers {
- [] -> 0
- _ -> do_product(numbers, 1)
- }
+/// Multiplies a list of Ints and returns the product.
+///
+/// ## Example
+///
+/// > product([2, 3, 4])
+/// 24
+///
+pub fn product(numbers: List(Int)) -> Int {
+ case numbers {
+ [] -> 0
+ _ -> do_product(numbers, 1)
}
+}
- fn do_product(numbers: List(Int), initial: Int) -> Int {
- case numbers {
- [] -> initial
- [x, ..rest] -> do_product(rest, x * initial)
- }
+fn do_product(numbers: List(Int), initial: Int) -> Int {
+ case numbers {
+ [] -> initial
+ [x, ..rest] -> do_product(rest, x * initial)
}
}
diff --git a/src/gleam/list.gleam b/src/gleam/list.gleam
index 33e32d1..682dd01 100644
--- a/src/gleam/list.gleam
+++ b/src/gleam/list.gleam
@@ -18,1560 +18,1616 @@
//// }
////
-if erlang {
- import gleam/int
- import gleam/pair
- import gleam/order.{Order}
+import gleam/int
+import gleam/pair
+import gleam/order.{Order}
- pub type List(elements) =
- List(elements)
+pub type List(elements) =
+ List(elements)
- /// An error value returned by the `strict_zip` function.
- ///
- pub type LengthMismatch {
- LengthMismatch
- }
+/// An error value returned by the `strict_zip` function.
+///
+pub type LengthMismatch {
+ LengthMismatch
+}
- /// Counts the number of elements in a given list.
- ///
- /// This function has to traverse the list to determine the number of elements,
- /// so it runs in linear time.
- ///
- /// This function is natively implemented by the virtual machine and is highly
- /// optimised.
- ///
- /// ## Examples
- ///
- /// > length([])
- /// 0
- ///
- /// > length([1])
- /// 1
- ///
- /// > length([1, 2])
- /// 2
- ///
- pub external fn length(of: List(a)) -> Int =
- "erlang" "length"
+/// Counts the number of elements in a given list.
+///
+/// This function has to traverse the list to determine the number of elements,
+/// so it runs in linear time.
+///
+/// This function is natively implemented by the virtual machine and is highly
+/// optimised.
+///
+/// ## Examples
+///
+/// > length([])
+/// 0
+///
+/// > length([1])
+/// 1
+///
+/// > length([1, 2])
+/// 2
+///
+pub fn length(of list: List(a)) -> Int {
+ do_length(list)
+}
- /// Creates a new list from a given list containing the same elements but in the
- /// opposite order.
- ///
- /// This function has to traverse the list to create the new reversed list, so
- /// it runs in linear time.
- ///
- /// This function is natively implemented by the virtual machine and is highly
- /// optimised.
- ///
- /// ## Examples
- ///
- /// > reverse([])
- /// []
- ///
- /// > reverse([1])
- /// [1]
- ///
- /// > reverse([1, 2])
- /// [2, 1]
- ///
- pub external fn reverse(List(a)) -> List(a) =
- "lists" "reverse"
+if erlang {
+ external fn do_length(List(a)) -> Int =
+ "erlang" "length"
+}
- /// Determines whether or not the list is empty.
- ///
- /// This function runs in constant time.
- ///
- /// ## Examples
- ///
- /// > is_empty([])
- /// True
- ///
- /// > is_empty([1])
- /// False
- ///
- /// > is_empty([1, 1])
- /// False
- ///
- pub fn is_empty(list: List(a)) -> Bool {
- list == []
+if javascript {
+ fn do_length(list: List(a)) -> Int {
+ do_length_acc(list, 0)
}
- /// Determines whether or not a given element exists within a given list.
- ///
- /// This function traverses the list to find the element, so it runs in linear
- /// time.
- ///
- /// ## Examples
- ///
- /// > [] |> contains(any: 0)
- /// True
- ///
- /// > [0] |> contains(any: 0)
- /// True
- ///
- /// > [1] |> contains(any: 0)
- /// False
- ///
- /// > [1, 1] |> contains(any: 0)
- /// False
- ///
- /// > [1, 0] |> contains(any: 0)
- /// True
- ///
- pub fn contains(list: List(a), any elem: a) -> Bool {
+ fn do_length_acc(list: List(a), count: Int) -> Int {
case list {
- [] -> False
- [head, ..rest] -> head == elem || contains(rest, elem)
+ [_, ..list] -> do_length_acc(list, count + 1)
+ _ -> count
}
}
+}
- /// Gets the first element from the start of the list, if there is one.
- ///
- /// ## Examples
- ///
- /// > head([])
- /// Error(Nil)
- ///
- /// > head([0])
- /// Ok(0)
- ///
- /// > head([1, 2])
- /// Ok(1)
- ///
- pub fn head(list: List(a)) -> Result(a, Nil) {
- case list {
- [] -> Error(Nil)
- [x, .._] -> Ok(x)
- }
+/// Creates a new list from a given list containing the same elements but in the
+/// opposite order.
+///
+/// This function has to traverse the list to create the new reversed list, so
+/// it runs in linear time.
+///
+/// This function is natively implemented by the virtual machine and is highly
+/// optimised.
+///
+/// ## Examples
+///
+/// > reverse([])
+/// []
+///
+/// > reverse([1])
+/// [1]
+///
+/// > reverse([1, 2])
+/// [2, 1]
+///
+pub fn reverse(xs: List(a)) -> List(a) {
+ do_reverse(xs)
+}
+
+if erlang {
+ external fn do_reverse(List(a)) -> List(a) =
+ "lists" "reverse"
+}
+
+if javascript {
+ pub fn do_reverse(list) {
+ do_reverse_acc(list, [])
}
- /// Gets the list minus the first element. If the list is empty `Error(Nil)` is
- /// returned.
- ///
- /// This function runs in constant time and does not make a copy of the list.
- ///
- /// ## Examples
- ///
- /// > tail([])
- /// Error(Nil)
- ///
- /// > tail([0])
- /// Ok([])
- ///
- /// > tail([1, 2])
- /// Ok([2])
- ///
- pub fn tail(list: List(a)) -> Result(List(a), Nil) {
- case list {
- [] -> Error(Nil)
- [_, ..xs] -> Ok(xs)
+ fn do_reverse_acc(remaining, accumulator) {
+ case remaining {
+ [] -> accumulator
+ [item, ..rest] -> do_reverse_acc(rest, [item, ..accumulator])
}
}
+}
- fn do_filter(list: List(a), fun: fn(a) -> Bool, acc: List(a)) -> List(a) {
- case list {
- [] -> reverse(acc)
- [x, ..xs] -> {
- let new_acc = case fun(x) {
- True -> [x, ..acc]
- False -> acc
- }
- do_filter(xs, fun, new_acc)
- }
- }
+/// Determines whether or not the list is empty.
+///
+/// This function runs in constant time.
+///
+/// ## Examples
+///
+/// > is_empty([])
+/// True
+///
+/// > is_empty([1])
+/// False
+///
+/// > is_empty([1, 1])
+/// False
+///
+pub fn is_empty(list: List(a)) -> Bool {
+ list == []
+}
+
+/// Determines whether or not a given element exists within a given list.
+///
+/// This function traverses the list to find the element, so it runs in linear
+/// time.
+///
+/// ## Examples
+///
+/// > [] |> contains(any: 0)
+/// True
+///
+/// > [0] |> contains(any: 0)
+/// True
+///
+/// > [1] |> contains(any: 0)
+/// False
+///
+/// > [1, 1] |> contains(any: 0)
+/// False
+///
+/// > [1, 0] |> contains(any: 0)
+/// True
+///
+pub fn contains(list: List(a), any elem: a) -> Bool {
+ case list {
+ [] -> False
+ [head, ..rest] -> head == elem || contains(rest, elem)
}
+}
- /// Returns a new list containing only the elements from the first list for
- /// which the given functions returns `True`.
- ///
- /// ## Examples
- ///
- /// > filter([2, 4, 6, 1], fn(x) { x > 2 })
- /// [4, 6]
- ///
- /// > 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, [])
+/// Gets the first element from the start of the list, if there is one.
+///
+/// ## Examples
+///
+/// > head([])
+/// Error(Nil)
+///
+/// > head([0])
+/// Ok(0)
+///
+/// > head([1, 2])
+/// Ok(1)
+///
+pub fn head(list: List(a)) -> Result(a, Nil) {
+ case list {
+ [] -> Error(Nil)
+ [x, .._] -> Ok(x)
}
+}
- fn do_filter_map(
- list: List(a),
- fun: fn(a) -> Result(b, e),
- acc: List(b),
- ) -> List(b) {
- case list {
- [] -> reverse(acc)
- [x, ..xs] -> {
- let new_acc = case fun(x) {
- Ok(x) -> [x, ..acc]
- Error(_) -> acc
- }
- do_filter_map(xs, fun, new_acc)
+/// Gets the list minus the first element. If the list is empty `Error(Nil)` is
+/// returned.
+///
+/// This function runs in constant time and does not make a copy of the list.
+///
+/// ## Examples
+///
+/// > tail([])
+/// Error(Nil)
+///
+/// > tail([0])
+/// Ok([])
+///
+/// > tail([1, 2])
+/// Ok([2])
+///
+pub fn tail(list: List(a)) -> Result(List(a), Nil) {
+ case list {
+ [] -> Error(Nil)
+ [_, ..xs] -> Ok(xs)
+ }
+}
+
+fn do_filter(list: List(a), fun: fn(a) -> Bool, acc: List(a)) -> List(a) {
+ case list {
+ [] -> reverse(acc)
+ [x, ..xs] -> {
+ let new_acc = case fun(x) {
+ True -> [x, ..acc]
+ False -> acc
}
+ do_filter(xs, fun, new_acc)
}
}
+}
- /// Returns a new list containing only the elements from the first list for
- /// which the given functions returns `Ok(_)`.
- ///
- /// ## Examples
- ///
- /// > filter_map([2, 4, 6, 1], Error)
- /// []
- ///
- /// > filter_map([2, 4, 6, 1], fn(x) { Ok(x + 1) })
- /// [3, 4, 6, 2]
- ///
- pub fn filter_map(list: List(a), with fun: fn(a) -> Result(b, e)) -> List(b) {
- do_filter_map(list, fun, [])
- }
+/// Returns a new list containing only the elements from the first list for
+/// which the given functions returns `True`.
+///
+/// ## Examples
+///
+/// > filter([2, 4, 6, 1], fn(x) { x > 2 })
+/// [4, 6]
+///
+/// > 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, [])
+}
- fn do_map(list: List(a), fun: fn(a) -> b, acc: List(b)) -> List(b) {
- case list {
- [] -> reverse(acc)
- [x, ..xs] -> do_map(xs, fun, [fun(x), ..acc])
+fn do_filter_map(
+ list: List(a),
+ fun: fn(a) -> Result(b, e),
+ acc: List(b),
+) -> List(b) {
+ case list {
+ [] -> reverse(acc)
+ [x, ..xs] -> {
+ let new_acc = case fun(x) {
+ Ok(x) -> [x, ..acc]
+ Error(_) -> acc
+ }
+ do_filter_map(xs, fun, new_acc)
}
}
+}
- /// Returns a new list containing only the elements of the first list after the
- /// function has been applied to each one.
- ///
- /// ## Examples
- ///
- /// > 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, [])
- }
+/// Returns a new list containing only the elements from the first list for
+/// which the given functions returns `Ok(_)`.
+///
+/// ## Examples
+///
+/// > filter_map([2, 4, 6, 1], Error)
+/// []
+///
+/// > filter_map([2, 4, 6, 1], fn(x) { Ok(x + 1) })
+/// [3, 4, 6, 2]
+///
+pub fn filter_map(list: List(a), with fun: fn(a) -> Result(b, e)) -> List(b) {
+ do_filter_map(list, fun, [])
+}
- /// Similar to map but also lets you pass around an accumulated value.
- ///
- /// ## Examples
- ///
- /// ```
- /// > map_fold(
- /// over: [1, 2, 3],
- /// from: 100,
- /// with: fn(i, memo) { #(i * 2, memo + i) }
- /// )
- /// #([2, 4, 6], 106)
- /// ```
- ///
- pub fn map_fold(
- over list: List(a),
- from memo: memo,
- with fun: fn(a, memo) -> #(b, memo),
- ) -> #(List(b), memo) {
- fold(
- over: list,
- from: #([], memo),
- with: fn(item, acc) {
- let #(items, current_memo) = acc
- let #(next_item, next_memo) = fun(item, current_memo)
- #([next_item, ..items], next_memo)
- },
- )
- |> pair.map_first(reverse)
+fn do_map(list: List(a), fun: fn(a) -> b, acc: List(b)) -> List(b) {
+ case list {
+ [] -> reverse(acc)
+ [x, ..xs] -> do_map(xs, fun, [fun(x), ..acc])
}
+}
- fn do_index_map(
- list: List(a),
- fun: fn(Int, a) -> b,
- index: Int,
- acc: List(b),
- ) -> List(b) {
- case list {
- [] -> reverse(acc)
- [x, ..xs] -> do_index_map(xs, fun, index + 1, [fun(index, x), ..acc])
- }
- }
+/// Returns a new list containing only the elements of the first list after the
+/// function has been applied to each one.
+///
+/// ## Examples
+///
+/// > 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, [])
+}
- /// Returns a new list containing only the elements of the first list after the
- /// function has been applied to each one and their index.
- ///
- /// The index starts at 0, so the first element is 0, the second is 1, and so
- /// on.
- ///
- /// ## Examples
- ///
- /// > index_map(["a", "b"], fn(i, x) { #(i, x) })
- /// [#(0, "a"), #(1, "b")]
- ///
- pub fn index_map(list: List(a), with fun: fn(Int, a) -> b) -> List(b) {
- do_index_map(list, fun, 0, [])
- }
+/// Similar to map but also lets you pass around an accumulated value.
+///
+/// ## Examples
+///
+/// ```
+/// > map_fold(
+/// over: [1, 2, 3],
+/// from: 100,
+/// with: fn(i, memo) { #(i * 2, memo + i) }
+/// )
+/// #([2, 4, 6], 106)
+/// ```
+///
+pub fn map_fold(
+ over list: List(a),
+ from memo: memo,
+ with fun: fn(a, memo) -> #(b, memo),
+) -> #(List(b), memo) {
+ fold(
+ over: list,
+ from: #([], memo),
+ with: fn(item, acc) {
+ let #(items, current_memo) = acc
+ let #(next_item, next_memo) = fun(item, current_memo)
+ #([next_item, ..items], next_memo)
+ },
+ )
+ |> pair.map_first(reverse)
+}
- fn do_try_map(
- list: List(a),
- fun: fn(a) -> Result(b, e),
- acc: List(b),
- ) -> Result(List(b), e) {
- case list {
- [] -> Ok(reverse(acc))
- [x, ..xs] ->
- case fun(x) {
- Ok(y) -> do_try_map(xs, fun, [y, ..acc])
- Error(error) -> Error(error)
- }
+fn do_index_map(
+ list: List(a),
+ fun: fn(Int, a) -> b,
+ index: Int,
+ acc: List(b),
+) -> List(b) {
+ case list {
+ [] -> reverse(acc)
+ [x, ..xs] -> {
+ let acc = [fun(index, x), ..acc]
+ do_index_map(xs, fun, index + 1, acc)
}
}
+}
- /// Takes a function that returns a Result applies it to each element in a
- /// given list in tern.
- ///
- /// If the function returns `Ok(new_value)` for all elements in the list then a
- /// list of the new values is returned.
- ///
- /// If the function returns `Error(reason)` for any of the elements then it is
- /// returned immediately. None of the elements in the list are processed after
- /// one returns an `Error`.
- ///
- /// ## Examples
- ///
- /// > try_map([1, 2, 3], fn(x) { Ok(x + 2) })
- /// Ok([3, 4, 5])
- ///
- /// > try_map([1, 2, 3], fn(x) { Error(0) })
- /// Error(0)
- ///
- /// > try_map([[1], [2, 3]], head)
- /// Ok([1, 2])
- ///
- /// > try_map([[1], [], [2]], head)
- /// Error(Nil)
- ///
- pub fn try_map(
- over list: List(a),
- with fun: fn(a) -> Result(b, e),
- ) -> Result(List(b), e) {
- do_try_map(list, fun, [])
- }
+/// Returns a new list containing only the elements of the first list after the
+/// function has been applied to each one and their index.
+///
+/// The index starts at 0, so the first element is 0, the second is 1, and so
+/// on.
+///
+/// ## Examples
+///
+/// > index_map(["a", "b"], fn(i, x) { #(i, x) })
+/// [#(0, "a"), #(1, "b")]
+///
+pub fn index_map(list: List(a), with fun: fn(Int, a) -> b) -> List(b) {
+ do_index_map(list, fun, 0, [])
+}
- /// Returns a list that is the given list with up to the given number of
- /// elements removed from the front of the list.
- ///
- /// If the element has less than the number of elements an empty list is
- /// returned.
- ///
- /// This function runs in linear time but does not copy the list.
- ///
- /// ## Examples
- ///
- /// > drop([1, 2, 3, 4], 2)
- /// [3, 4]
- ///
- /// > drop([1, 2, 3, 4], 9)
- /// []
- ///
- pub fn drop(from list: List(a), up_to n: Int) -> List(a) {
- case n <= 0 {
- True -> list
- False ->
- case list {
- [] -> []
- [_, ..xs] -> drop(xs, n - 1)
- }
- }
+fn do_try_map(
+ list: List(a),
+ fun: fn(a) -> Result(b, e),
+ acc: List(b),
+) -> Result(List(b), e) {
+ case list {
+ [] -> Ok(reverse(acc))
+ [x, ..xs] ->
+ case fun(x) {
+ Ok(y) -> do_try_map(xs, fun, [y, ..acc])
+ Error(error) -> Error(error)
+ }
}
+}
- fn do_take(list: List(a), n: Int, acc: List(a)) -> List(a) {
- case n <= 0 {
- True -> reverse(acc)
- False ->
- case list {
- [] -> reverse(acc)
- [x, ..xs] -> do_take(xs, n - 1, [x, ..acc])
- }
- }
- }
+/// Takes a function that returns a Result applies it to each element in a
+/// given list in tern.
+///
+/// If the function returns `Ok(new_value)` for all elements in the list then a
+/// list of the new values is returned.
+///
+/// If the function returns `Error(reason)` for any of the elements then it is
+/// returned immediately. None of the elements in the list are processed after
+/// one returns an `Error`.
+///
+/// ## Examples
+///
+/// > try_map([1, 2, 3], fn(x) { Ok(x + 2) })
+/// Ok([3, 4, 5])
+///
+/// > try_map([1, 2, 3], fn(x) { Error(0) })
+/// Error(0)
+///
+/// > try_map([[1], [2, 3]], head)
+/// Ok([1, 2])
+///
+/// > try_map([[1], [], [2]], head)
+/// Error(Nil)
+///
+pub fn try_map(
+ over list: List(a),
+ with fun: fn(a) -> Result(b, e),
+) -> Result(List(b), e) {
+ do_try_map(list, fun, [])
+}
- /// Returns a list containing the first given number of elements from the given
- /// list.
- ///
- /// If the element has less than the number of elements then the full list is
- /// returned.
- ///
- /// This function runs in linear time but does not copy the list.
- ///
- /// ## Examples
- ///
- /// > take([1, 2, 3, 4], 2)
- /// [1, 2]
- ///
- /// > 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, [])
+/// Returns a list that is the given list with up to the given number of
+/// elements removed from the front of the list.
+///
+/// If the element has less than the number of elements an empty list is
+/// returned.
+///
+/// This function runs in linear time but does not copy the list.
+///
+/// ## Examples
+///
+/// > drop([1, 2, 3, 4], 2)
+/// [3, 4]
+///
+/// > drop([1, 2, 3, 4], 9)
+/// []
+///
+pub fn drop(from list: List(a), up_to n: Int) -> List(a) {
+ case n <= 0 {
+ True -> list
+ False ->
+ case list {
+ [] -> []
+ [_, ..xs] -> drop(xs, n - 1)
+ }
}
+}
- /// Returns a new empty list.
- ///
- /// ## Examples
- ///
- /// > new()
- /// []
- ///
- pub fn new() -> List(a) {
- []
+fn do_take(list: List(a), n: Int, acc: List(a)) -> List(a) {
+ case n <= 0 {
+ True -> reverse(acc)
+ False ->
+ case list {
+ [] -> reverse(acc)
+ [x, ..xs] -> do_take(xs, n - 1, [x, ..acc])
+ }
}
+}
- /// Joins one list onto the end of another.
- ///
- /// This function runs in linear time, and it traverses and copies the first
- /// list.
- ///
- /// ## Examples
- ///
- /// > append([1, 2], [3])
- /// [1, 2, 3]
- ///
- pub external fn append(List(a), List(a)) -> List(a) =
- "lists" "append"
+/// Returns a list containing the first given number of elements from the given
+/// list.
+///
+/// If the element has less than the number of elements then the full list is
+/// returned.
+///
+/// This function runs in linear time but does not copy the list.
+///
+/// ## Examples
+///
+/// > take([1, 2, 3, 4], 2)
+/// [1, 2]
+///
+/// > 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, [])
+}
- fn do_flatten(lists: List(List(a)), acc: List(a)) -> List(a) {
- case lists {
- [] -> acc
- [l, ..rest] -> do_flatten(rest, append(acc, l))
- }
- }
+/// Returns a new empty list.
+///
+/// ## Examples
+///
+/// > new()
+/// []
+///
+pub fn new() -> List(a) {
+ []
+}
- /// Flattens a list of lists into a single list.
- ///
- /// This function runs in linear time, and it traverses and copies all the
- /// inner lists.
- ///
- /// ## Examples
- ///
- /// > flatten([[1], [2, 3], []])
- /// [1, 2, 3]
- ///
- pub fn flatten(lists: List(List(a))) -> List(a) {
- do_flatten(lists, [])
- }
+/// Joins one list onto the end of another.
+///
+/// This function runs in linear time, and it traverses and copies the first
+/// list.
+///
+/// ## Examples
+///
+/// > append([1, 2], [3])
+/// [1, 2, 3]
+///
+pub fn append(first: List(a), second: List(a)) -> List(a) {
+ do_append(first, second)
+}
- /// Map and flatten the result
- ///
- /// ## Examples
- ///
- /// ```
- /// > flat_map([2, 4, 6], fn(x) { [x, x + 1] })
- /// [2, 3, 4, 5, 6, 7]
- /// ```
- ///
- pub fn flat_map(over list: List(a), with fun: fn(a) -> List(b)) -> List(b) {
- map(list, fun)
- |> flatten
- }
+if erlang {
+ external fn do_append(List(a), List(a)) -> List(a) =
+ "lists" "append"
+}
- /// Reduces a list of elements into a single value by calling a given function
- /// on each element, going from left to right.
- ///
- /// `fold([1, 2, 3], 0, add)` is the equivalent of `add(3, add(2, add(1, 0)))`.
- ///
- /// This function runs in linear time.
- ///
- pub fn fold(over list: List(a), from initial: b, with fun: fn(a, b) -> b) -> b {
- case list {
- [] -> initial
- [x, ..rest] -> fold(rest, fun(x, initial), fun)
- }
+if javascript {
+ fn do_append(first: List(a), second: List(a)) -> List(a) {
+ do_append_acc(reverse(first), second)
}
- /// Reduces a list of elements into a single value by calling a given function
- /// on each element, going from right to left.
- ///
- /// `fold_right([1, 2, 3], 0, add)` is the equivalent of
- /// `add(1, add(2, add(3, 0)))`.
- ///
- /// This function runs in linear time.
- ///
- /// Unlike `fold` this function is not tail recursive. Where possible use
- /// `fold` instead as it will use less memory.
- ///
- pub fn fold_right(
- over list: List(a),
- from initial: b,
- with fun: fn(a, b) -> b,
- ) -> b {
- case list {
- [] -> initial
- [x, ..rest] -> fun(x, fold_right(rest, initial, fun))
+ fn do_append_acc(remaining: List(a), second: List(a)) -> List(a) {
+ case remaining {
+ [] -> second
+ [item, ..rest] -> do_append(rest, [item, ..second])
}
}
+}
- fn do_index_fold(
- over: List(a),
- acc: b,
- with: fn(Int, a, b) -> b,
- index: Int,
- ) -> b {
- case over {
- [] -> acc
- [first, ..rest] ->
- do_index_fold(rest, with(index, first, acc), with, index + 1)
- }
+fn do_flatten(lists: List(List(a)), acc: List(a)) -> List(a) {
+ case lists {
+ [] -> acc
+ [l, ..rest] -> do_flatten(rest, append(acc, l))
}
+}
- /// Like fold but the folding function also receives the index of the current element.
- ///
- /// ## Examples
- ///
- /// ```
- /// ["a", "b", "c"]
- /// |> list.index_fold([], fn(index, item, acc) { ... })
- /// ```
- ///
- pub fn index_fold(
- over over: List(a),
- from initial: b,
- with fun: fn(Int, a, b) -> b,
- ) -> b {
- do_index_fold(over, initial, fun, 0)
- }
+/// Flattens a list of lists into a single list.
+///
+/// This function runs in linear time, and it traverses and copies all the
+/// inner lists.
+///
+/// ## Examples
+///
+/// > flatten([[1], [2, 3], []])
+/// [1, 2, 3]
+///
+pub fn flatten(lists: List(List(a))) -> List(a) {
+ do_flatten(lists, [])
+}
- /// A variant of fold that might fail.
- ///
- /// The folding function should return `Result(accumulator, error)
- /// If the returned value is `Ok(accumulator)` try_fold will try the next value in the list.
- /// If the returned value is `Error(error)` try_fold will stop and return that error.
- ///
- /// ## Examples
- ///
- /// ```
- /// [1, 2, 3, 4]
- /// |> try_fold(0, fn(i, acc) {
- /// case i < 3 {
- /// True -> Ok(acc + i)
- /// False -> Error(Nil)
- /// }
- /// })
- /// ```
- ///
- pub fn try_fold(
- over collection: List(a),
- from accumulator: b,
- with fun: fn(a, b) -> Result(b, e),
- ) -> Result(b, e) {
- case collection {
- [] -> Ok(accumulator)
- [first, ..rest] ->
- case fun(first, accumulator) {
- Ok(next_accumulator) -> try_fold(rest, next_accumulator, fun)
- Error(err) -> Error(err)
- }
- }
- }
+/// Map and flatten the result
+///
+/// ## Examples
+///
+/// ```
+/// > flat_map([2, 4, 6], fn(x) { [x, x + 1] })
+/// [2, 3, 4, 5, 6, 7]
+/// ```
+///
+pub fn flat_map(over list: List(a), with fun: fn(a) -> List(b)) -> List(b) {
+ map(list, fun)
+ |> flatten
+}
- pub type ContinueOrStop(a) {
- Continue(a)
- Stop(a)
+/// Reduces a list of elements into a single value by calling a given function
+/// on each element, going from left to right.
+///
+/// `fold([1, 2, 3], 0, add)` is the equivalent of `add(3, add(2, add(1, 0)))`.
+///
+/// This function runs in linear time.
+///
+pub fn fold(over list: List(a), from initial: b, with fun: fn(a, b) -> b) -> b {
+ case list {
+ [] -> initial
+ [x, ..rest] -> fold(rest, fun(x, initial), fun)
}
+}
- /// A variant of fold that allows to stop folding earlier.
- ///
- /// The folding function should return `ContinueOrStop(accumulator)
- /// If the returned value is `Continue(accumulator)` fold_until will try the next value in the list.
- /// If the returned value is `Stop(accumulator)` fold_until will stop and return that accumulator.
- ///
- /// ## Examples
- ///
- /// ```
- /// [1, 2, 3, 4]
- /// |> fold_until(0, fn(i, acc) {
- /// case i < 3 {
- /// True -> Continue(acc + i)
- /// False -> Stop(acc)
- /// }
- /// })
- /// ```
- ///
- pub fn fold_until(
- over collection: List(a),
- from accumulator: b,
- with fun: fn(a, b) -> ContinueOrStop(b),
- ) -> b {
- case collection {
- [] -> accumulator
- [first, ..rest] ->
- case fun(first, accumulator) {
- Continue(next_accumulator) -> fold_until(rest, next_accumulator, fun)
- Stop(b) -> b
- }
- }
+/// Reduces a list of elements into a single value by calling a given function
+/// on each element, going from right to left.
+///
+/// `fold_right([1, 2, 3], 0, add)` is the equivalent of
+/// `add(1, add(2, add(3, 0)))`.
+///
+/// This function runs in linear time.
+///
+/// Unlike `fold` this function is not tail recursive. Where possible use
+/// `fold` instead as it will use less memory.
+///
+pub fn fold_right(
+ over list: List(a),
+ from initial: b,
+ with fun: fn(a, b) -> b,
+) -> b {
+ case list {
+ [] -> initial
+ [x, ..rest] -> fun(x, fold_right(rest, initial, fun))
}
+}
- /// Finds the first element in a given list for which the given function returns
- /// True.
- ///
- /// Returns `Error(Nil)` if no the function does not return True for any of the
- /// elements.
- ///
- /// ## Examples
- ///
- /// > find([1, 2, 3], fn(x) { x > 2 })
- /// Ok(3)
- ///
- /// > find([1, 2, 3], fn(x) { x > 4 })
- /// Error(Nil)
- ///
- /// > find([], fn(x) { True })
- /// Error(Nil)
- ///
- pub fn find(
- in haystack: List(a),
- one_that is_desired: fn(a) -> Bool,
- ) -> Result(a, Nil) {
- case haystack {
- [] -> Error(Nil)
- [x, ..rest] ->
- case is_desired(x) {
- True -> Ok(x)
- _ -> find(in: rest, one_that: is_desired)
- }
- }
+fn do_index_fold(
+ over: List(a),
+ acc: b,
+ with: fn(Int, a, b) -> b,
+ index: Int,
+) -> b {
+ case over {
+ [] -> acc
+ [first, ..rest] ->
+ do_index_fold(rest, with(index, first, acc), with, index + 1)
}
+}
- /// Finds the first element in a given list for which the given function returns
- /// `Ok(new_value)` and return the new value for that element.
- ///
- /// Returns `Error(Nil)` if no the function does not return Ok for any of the
- /// elements.
- ///
- /// ## Examples
- ///
- /// > find_map([[], [2], [3]], head)
- /// Ok(2)
- ///
- /// > find_map([[], []], head)
- /// Error(Nil)
- ///
- /// > find_map([], head)
- /// Error(Nil)
- ///
- pub fn find_map(
- in haystack: List(a),
- with fun: fn(a) -> Result(b, c),
- ) -> Result(b, Nil) {
- case haystack {
- [] -> Error(Nil)
- [x, ..rest] ->
- case fun(x) {
- Ok(x) -> Ok(x)
- _ -> find_map(in: rest, with: fun)
- }
- }
- }
+/// Like fold but the folding function also receives the index of the current element.
+///
+/// ## Examples
+///
+/// ```
+/// ["a", "b", "c"]
+/// |> list.index_fold([], fn(index, item, acc) { ... })
+/// ```
+///
+pub fn index_fold(
+ over over: List(a),
+ from initial: b,
+ with fun: fn(Int, a, b) -> b,
+) -> b {
+ do_index_fold(over, initial, fun, 0)
+}
- /// Returns True if the given function returns True for all the elements in
- /// the given list. If the function returns False for any of the elements it
- /// immediately returns False without checking the rest of the list.
- ///
- /// ## Examples
- ///
- /// > all([], fn(x) { x > 3 })
- /// True
- ///
- /// > all([4, 5], fn(x) { x > 3 })
- /// True
- ///
- /// > all([4, 3], fn(x) { x > 3 })
- /// False
- ///
- pub fn all(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool {
- case list {
- [] -> True
- [x, ..rest] -> predicate(x) && all(rest, predicate)
- }
+/// A variant of fold that might fail.
+///
+/// The folding function should return `Result(accumulator, error)
+/// If the returned value is `Ok(accumulator)` try_fold will try the next value in the list.
+/// If the returned value is `Error(error)` try_fold will stop and return that error.
+///
+/// ## Examples
+///
+/// ```
+/// [1, 2, 3, 4]
+/// |> try_fold(0, fn(i, acc) {
+/// case i < 3 {
+/// True -> Ok(acc + i)
+/// False -> Error(Nil)
+/// }
+/// })
+/// ```
+///
+pub fn try_fold(
+ over collection: List(a),
+ from accumulator: b,
+ with fun: fn(a, b) -> Result(b, e),
+) -> Result(b, e) {
+ case collection {
+ [] -> Ok(accumulator)
+ [first, ..rest] ->
+ case fun(first, accumulator) {
+ Ok(next_accumulator) -> try_fold(rest, next_accumulator, fun)
+ Error(err) -> Error(err)
+ }
}
+}
- /// Returns True if the given function returns True for any the elements in
- /// the given list. If the function returns True for any of the elements it
- /// immediately returns True without checking the rest of the list.
- ///
- /// ## Examples
- ///
- /// > any([], fn(x) { x > 3 })
- /// False
- ///
- /// > any([4, 5], fn(x) { x > 3 })
- /// False
- ///
- /// > any([4, 3], fn(x) { x > 3 })
- /// True
- ///
- /// > any([3, 4], fn(x) { x > 3 })
- /// True
- ///
- pub fn any(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool {
- case list {
- [] -> False
- [x, ..rest] -> predicate(x) || any(rest, predicate)
- }
- }
+pub type ContinueOrStop(a) {
+ Continue(a)
+ Stop(a)
+}
- fn do_zip(xs: List(a), ys: List(b), acc: List(#(a, b))) -> List(#(a, b)) {
- case xs, ys {
- [x, ..xs], [y, ..ys] -> do_zip(xs, ys, [#(x, y), ..acc])
- _, _ -> reverse(acc)
- }
+/// A variant of fold that allows to stop folding earlier.
+///
+/// The folding function should return `ContinueOrStop(accumulator)
+/// If the returned value is `Continue(accumulator)` fold_until will try the next value in the list.
+/// If the returned value is `Stop(accumulator)` fold_until will stop and return that accumulator.
+///
+/// ## Examples
+///
+/// ```
+/// [1, 2, 3, 4]
+/// |> fold_until(0, fn(i, acc) {
+/// case i < 3 {
+/// True -> Continue(acc + i)
+/// False -> Stop(acc)
+/// }
+/// })
+/// ```
+///
+pub fn fold_until(
+ over collection: List(a),
+ from accumulator: b,
+ with fun: fn(a, b) -> ContinueOrStop(b),
+) -> b {
+ case collection {
+ [] -> accumulator
+ [first, ..rest] ->
+ case fun(first, accumulator) {
+ Continue(next_accumulator) -> fold_until(rest, next_accumulator, fun)
+ Stop(b) -> b
+ }
}
+}
- /// Takes two lists and returns a single list of 2 item tuples.
- ///
- /// If one of the lists is longer than the other the remaining elements from
- /// the longer list are not used.
- ///
- /// ## Examples
- ///
- /// > zip([], [])
- /// []
- ///
- /// > zip([1, 2], [3])
- /// [#(1, 3)]
- ///
- /// > zip([1], [3, 4])
- /// [#(1, 3)]
- ///
- /// > zip([1, 2], [3, 4])
- /// [#(1, 3), #(2, 4)]
- ///
- pub fn zip(xs: List(a), ys: List(b)) -> List(#(a, b)) {
- do_zip(xs, ys, [])
+/// Finds the first element in a given list for which the given function returns
+/// True.
+///
+/// Returns `Error(Nil)` if no the function does not return True for any of the
+/// elements.
+///
+/// ## Examples
+///
+/// > find([1, 2, 3], fn(x) { x > 2 })
+/// Ok(3)
+///
+/// > find([1, 2, 3], fn(x) { x > 4 })
+/// Error(Nil)
+///
+/// > find([], fn(x) { True })
+/// Error(Nil)
+///
+pub fn find(
+ in haystack: List(a),
+ one_that is_desired: fn(a) -> Bool,
+) -> Result(a, Nil) {
+ case haystack {
+ [] -> Error(Nil)
+ [x, ..rest] ->
+ case is_desired(x) {
+ True -> Ok(x)
+ _ -> find(in: rest, one_that: is_desired)
+ }
}
+}
- /// Takes two lists and returns a single list of 2 item tuples.
- ///
- /// If one of the lists is longer than the other an Error is returned.
- ///
- /// ## Examples
- ///
- /// > strict_zip([], [])
- /// Ok([])
- ///
- /// > strict_zip([1, 2], [3])
- /// Error(LengthMismatch)
- ///
- /// > strict_zip([1], [3, 4])
- /// Error(LengthMismatch)
- ///
- /// > strict_zip([1, 2], [3, 4])
- /// Ok([#(1, 3), #(2, 4)])
- ///
- pub fn strict_zip(
- l1: List(a),
- l2: List(b),
- ) -> Result(List(#(a, b)), LengthMismatch) {
- case length(of: l1) == length(of: l2) {
- True -> Ok(zip(l1, l2))
- False -> Error(LengthMismatch)
- }
+/// Finds the first element in a given list for which the given function returns
+/// `Ok(new_value)` and return the new value for that element.
+///
+/// Returns `Error(Nil)` if no the function does not return Ok for any of the
+/// elements.
+///
+/// ## Examples
+///
+/// > find_map([[], [2], [3]], head)
+/// Ok(2)
+///
+/// > find_map([[], []], head)
+/// Error(Nil)
+///
+/// > find_map([], head)
+/// Error(Nil)
+///
+pub fn find_map(
+ in haystack: List(a),
+ with fun: fn(a) -> Result(b, c),
+) -> Result(b, Nil) {
+ case haystack {
+ [] -> Error(Nil)
+ [x, ..rest] ->
+ case fun(x) {
+ Ok(x) -> Ok(x)
+ _ -> find_map(in: rest, with: fun)
+ }
}
+}
- fn do_unzip(input, xs, ys) {
- case input {
- [] -> #(reverse(xs), reverse(ys))
- [#(x, y), ..rest] -> do_unzip(rest, [x, ..xs], [y, ..ys])
- }
+/// Returns True if the given function returns True for all the elements in
+/// the given list. If the function returns False for any of the elements it
+/// immediately returns False without checking the rest of the list.
+///
+/// ## Examples
+///
+/// > all([], fn(x) { x > 3 })
+/// True
+///
+/// > all([4, 5], fn(x) { x > 3 })
+/// True
+///
+/// > all([4, 3], fn(x) { x > 3 })
+/// False
+///
+pub fn all(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool {
+ case list {
+ [] -> True
+ [x, ..rest] -> predicate(x) && all(rest, predicate)
}
+}
- /// Takes a single list of 2 item tuples and returns two lists.
- ///
- /// ## Examples
- ///
- /// > unzip([#(1, 2), #(3, 4)])
- /// #([1, 3], [2, 4])
- ///
- /// > unzip([])
- /// #([], [])
- ///
- pub fn unzip(input: List(#(a, b))) -> #(List(a), List(b)) {
- do_unzip(input, [], [])
+/// Returns True if the given function returns True for any the elements in
+/// the given list. If the function returns True for any of the elements it
+/// immediately returns True without checking the rest of the list.
+///
+/// ## Examples
+///
+/// > any([], fn(x) { x > 3 })
+/// False
+///
+/// > any([4, 5], fn(x) { x > 3 })
+/// False
+///
+/// > any([4, 3], fn(x) { x > 3 })
+/// True
+///
+/// > any([3, 4], fn(x) { x > 3 })
+/// True
+///
+pub fn any(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool {
+ case list {
+ [] -> False
+ [x, ..rest] -> predicate(x) || any(rest, predicate)
}
+}
- fn do_intersperse(list: List(a), separator: a, acc: List(a)) -> List(a) {
- case list {
- [] -> reverse(acc)
- [x, ..rest] -> do_intersperse(rest, separator, [x, separator, ..acc])
- }
+fn do_zip(xs: List(a), ys: List(b), acc: List(#(a, b))) -> List(#(a, b)) {
+ case xs, ys {
+ [x, ..xs], [y, ..ys] -> do_zip(xs, ys, [#(x, y), ..acc])
+ _, _ -> reverse(acc)
}
+}
- /// Inserts a given value between each existing element in a given list.
- ///
- /// This function runs in linear time and copies the list.
- ///
- /// ## Examples
- ///
- /// > intersperse([1, 1, 1], 2)
- /// [1, 2, 1, 2, 1]
- ///
- /// > intersperse([], 2)
- /// []
- ///
- pub fn intersperse(list: List(a), with elem: a) -> List(a) {
- case list {
- [] | [_] -> list
- [x, ..rest] -> do_intersperse(rest, elem, [x])
- }
+/// Takes two lists and returns a single list of 2 item tuples.
+///
+/// If one of the lists is longer than the other the remaining elements from
+/// the longer list are not used.
+///
+/// ## Examples
+///
+/// > zip([], [])
+/// []
+///
+/// > zip([1, 2], [3])
+/// [#(1, 3)]
+///
+/// > zip([1], [3, 4])
+/// [#(1, 3)]
+///
+/// > zip([1, 2], [3, 4])
+/// [#(1, 3), #(2, 4)]
+///
+pub fn zip(xs: List(a), ys: List(b)) -> List(#(a, b)) {
+ do_zip(xs, ys, [])
+}
+
+/// Takes two lists and returns a single list of 2 item tuples.
+///
+/// If one of the lists is longer than the other an Error is returned.
+///
+/// ## Examples
+///
+/// > strict_zip([], [])
+/// Ok([])
+///
+/// > strict_zip([1, 2], [3])
+/// Error(LengthMismatch)
+///
+/// > strict_zip([1], [3, 4])
+/// Error(LengthMismatch)
+///
+/// > strict_zip([1, 2], [3, 4])
+/// Ok([#(1, 3), #(2, 4)])
+///
+pub fn strict_zip(
+ l1: List(a),
+ l2: List(b),
+) -> Result(List(#(a, b)), LengthMismatch) {
+ case length(of: l1) == length(of: l2) {
+ True -> Ok(zip(l1, l2))
+ False -> Error(LengthMismatch)
}
+}
- /// Returns the element in the Nth position in the list, with 0 being the first
- /// position.
- ///
- /// Error(Nil) is returned if the list is not long enough for the given index.
- ///
- /// ## Examples
- ///
- /// > at([1, 2, 3], 1)
- /// Ok(2)
- ///
- /// > at([1, 2, 3], 5)
- /// Error(Nil)
- ///
- pub fn at(in list: List(a), get index: Int) -> Result(a, Nil) {
- case index < 0 {
- True -> Error(Nil)
- False ->
- case list {
- [] -> Error(Nil)
- [x, ..rest] ->
- case index == 0 {
- True -> Ok(x)
- False -> at(rest, index - 1)
- }
- }
- }
+fn do_unzip(input, xs, ys) {
+ case input {
+ [] -> #(reverse(xs), reverse(ys))
+ [#(x, y), ..rest] -> do_unzip(rest, [x, ..xs], [y, ..ys])
}
+}
- /// Removes any duplicate elements from a given list.
- ///
- /// This function returns in log-linear time (n log n).
- ///
- /// ## Examples
- ///
- /// > unique([1, 1, 1, 4, 7, 3, 3, 4])
- /// [1, 4, 7, 3]
- ///
- pub fn unique(list: List(a)) -> List(a) {
- case list {
- [] -> []
- [x, ..rest] -> [x, ..unique(filter(rest, fn(y) { y != x }))]
- }
+/// Takes a single list of 2 item tuples and returns two lists.
+///
+/// ## Examples
+///
+/// > unzip([#(1, 2), #(3, 4)])
+/// #([1, 3], [2, 4])
+///
+/// > unzip([])
+/// #([], [])
+///
+pub fn unzip(input: List(#(a, b))) -> #(List(a), List(b)) {
+ do_unzip(input, [], [])
+}
+
+fn do_intersperse(list: List(a), separator: a, acc: List(a)) -> List(a) {
+ case list {
+ [] -> reverse(acc)
+ [x, ..rest] -> do_intersperse(rest, separator, [x, separator, ..acc])
}
+}
- fn merge_sort(a: List(a), b: List(a), compare: fn(a, a) -> Order) -> List(a) {
- case a, b {
- [], _ -> b
- _, [] -> a
- [ax, ..ar], [bx, ..br] ->
- case compare(ax, bx) {
- order.Lt -> [ax, ..merge_sort(ar, b, compare)]
- _ -> [bx, ..merge_sort(a, br, compare)]
- }
- }
+/// Inserts a given value between each existing element in a given list.
+///
+/// This function runs in linear time and copies the list.
+///
+/// ## Examples
+///
+/// > intersperse([1, 1, 1], 2)
+/// [1, 2, 1, 2, 1]
+///
+/// > intersperse([], 2)
+/// []
+///
+pub fn intersperse(list: List(a), with elem: a) -> List(a) {
+ case list {
+ [] | [_] -> list
+ [x, ..rest] -> do_intersperse(rest, elem, [x])
}
+}
- fn do_sort(
- list: List(a),
- compare: fn(a, a) -> Order,
- list_length: Int,
- ) -> List(a) {
- case list_length < 2 {
- True -> list
- False -> {
- let split_length = list_length / 2
- let a_list = take(list, split_length)
- let b_list = drop(list, split_length)
- merge_sort(
- do_sort(a_list, compare, split_length),
- do_sort(b_list, compare, list_length - split_length),
- compare,
- )
+/// Returns the element in the Nth position in the list, with 0 being the first
+/// position.
+///
+/// Error(Nil) is returned if the list is not long enough for the given index.
+///
+/// ## Examples
+///
+/// > at([1, 2, 3], 1)
+/// Ok(2)
+///
+/// > at([1, 2, 3], 5)
+/// Error(Nil)
+///
+pub fn at(in list: List(a), get index: Int) -> Result(a, Nil) {
+ case index < 0 {
+ True -> Error(Nil)
+ False ->
+ case list {
+ [] -> Error(Nil)
+ [x, ..rest] ->
+ case index == 0 {
+ True -> Ok(x)
+ False -> at(rest, index - 1)
+ }
}
- }
}
+}
- /// Sorts 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], by: int.compare)
- /// [1, 2, 3, 4, 4, 5, 6]
- ///
- pub fn sort(list: List(a), by compare: fn(a, a) -> Order) -> List(a) {
- do_sort(list, compare, length(list))
+/// Removes any duplicate elements from a given list.
+///
+/// This function returns in log-linear time (n log n).
+///
+/// ## Examples
+///
+/// > unique([1, 1, 1, 4, 7, 3, 3, 4])
+/// [1, 4, 7, 3]
+///
+pub fn unique(list: List(a)) -> List(a) {
+ case list {
+ [] -> []
+ [x, ..rest] -> [x, ..unique(filter(rest, fn(y) { y != x }))]
}
+}
- /// Creates a list of ints ranging from a given start and finish.
- ///
- /// ## Examples
- ///
- /// > range(0, 0)
- /// []
- ///
- /// > range(0, 5)
- /// [0, 1, 2, 3, 4]
- ///
- /// > 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 -> []
- order.Gt -> [start, ..range(start - 1, stop)]
- order.Lt -> [start, ..range(start + 1, stop)]
- }
+fn merge_sort(a: List(a), b: List(a), compare: fn(a, a) -> Order) -> List(a) {
+ case a, b {
+ [], _ -> b
+ _, [] -> a
+ [ax, ..ar], [bx, ..br] ->
+ case compare(ax, bx) {
+ order.Lt -> [ax, ..merge_sort(ar, b, compare)]
+ _ -> [bx, ..merge_sort(a, br, compare)]
+ }
}
+}
- fn do_repeat(a: a, times: Int, acc: List(a)) -> List(a) {
- case times <= 0 {
- True -> acc
- False -> do_repeat(a, times - 1, [a, ..acc])
+fn do_sort(
+ list: List(a),
+ compare: fn(a, a) -> Order,
+ list_length: Int,
+) -> List(a) {
+ case list_length < 2 {
+ True -> list
+ False -> {
+ let split_length = list_length / 2
+ let a_list = take(list, split_length)
+ let b_list = drop(list, split_length)
+ merge_sort(
+ do_sort(a_list, compare, split_length),
+ do_sort(b_list, compare, list_length - split_length),
+ compare,
+ )
}
}
+}
- /// Builds a list of a given value a given number of times.
- ///
- /// ## Examples
- ///
- /// > repeat("a", times: 0)
- /// []
- ///
- /// > repeat("a", times: 5)
- /// ["a", "a", "a", "a", "a"]
- ///
- pub fn repeat(item a: a, times times: Int) -> List(a) {
- do_repeat(a, times, [])
- }
+/// Sorts 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], by: int.compare)
+/// [1, 2, 3, 4, 4, 5, 6]
+///
+pub fn sort(list: List(a), by compare: fn(a, a) -> Order) -> List(a) {
+ do_sort(list, compare, length(list))
+}
- fn do_split(list: List(a), n: Int, taken: List(a)) -> #(List(a), List(a)) {
- case n <= 0 {
- True -> #(reverse(taken), list)
- False ->
- case list {
- [] -> #(reverse(taken), [])
- [x, ..xs] -> do_split(xs, n - 1, [x, ..taken])
- }
- }
+/// Creates a list of ints ranging from a given start and finish.
+///
+/// ## Examples
+///
+/// > range(0, 0)
+/// []
+///
+/// > range(0, 5)
+/// [0, 1, 2, 3, 4]
+///
+/// > 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 -> []
+ order.Gt -> [start, ..range(start - 1, stop)]
+ order.Lt -> [start, ..range(start + 1, stop)]
}
+}
- /// Splits 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)
- /// #([], [6, 7, 8, 9])
- ///
- /// > split([6, 7, 8, 9], 2)
- /// #([6, 7], [8, 9])
- ///
- /// > split([6, 7, 8, 9], 4)
- /// #([6, 7, 8, 9], [])
- ///
- pub fn split(list list: List(a), at index: Int) -> #(List(a), List(a)) {
- do_split(list, index, [])
+fn do_repeat(a: a, times: Int, acc: List(a)) -> List(a) {
+ case times <= 0 {
+ True -> acc
+ False -> do_repeat(a, times - 1, [a, ..acc])
}
+}
- fn do_split_while(
- list: List(a),
- f: fn(a) -> Bool,
- acc: List(a),
- ) -> #(List(a), List(a)) {
- case list {
- [] -> #(reverse(acc), [])
- [x, ..xs] ->
- case f(x) {
- False -> #(reverse(acc), list)
- _ -> do_split_while(xs, f, [x, ..acc])
- }
- }
- }
+/// Builds a list of a given value a given number of times.
+///
+/// ## Examples
+///
+/// > repeat("a", times: 0)
+/// []
+///
+/// > repeat("a", times: 5)
+/// ["a", "a", "a", "a", "a"]
+///
+pub fn repeat(item a: a, times times: Int) -> List(a) {
+ do_repeat(a, times, [])
+}
- /// Splits 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 })
- /// #([1, 2, 3], [4, 5])
- ///
- /// > split_while([1, 2, 3, 4, 5], fn(x) { x <= 5 })
- /// #([1, 2, 3, 4, 5], [])
- ///
- pub fn split_while(
- list list: List(a),
- satisfying predicate: fn(a) -> Bool,
- ) -> #(List(a), List(a)) {
- do_split_while(list, predicate, [])
+fn do_split(list: List(a), n: Int, taken: List(a)) -> #(List(a), List(a)) {
+ case n <= 0 {
+ True -> #(reverse(taken), list)
+ False ->
+ case list {
+ [] -> #(reverse(taken), [])
+ [x, ..xs] -> do_split(xs, n - 1, [x, ..taken])
+ }
}
+}
- /// Given a list of 2 element tuples, finds the first tuple that has a given
- /// key as the first element and returns 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([#("a", 0), #("b", 1)], "a")
- /// Ok(0)
- ///
- /// > key_find([#("a", 0), #("b", 1)], "b")
- /// Ok(1)
- ///
- /// > key_find([#("a", 0), #("b", 1)], "c")
- /// Error(Nil)
- ///
- pub fn key_find(
- in keyword_list: List(#(k, v)),
- find desired_key: k,
- ) -> Result(v, Nil) {
- find_map(
- keyword_list,
- fn(keyword) {
- let #(key, value) = keyword
- case key == desired_key {
- True -> Ok(value)
- False -> Error(Nil)
- }
- },
- )
- }
+/// Splits 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)
+/// #([], [6, 7, 8, 9])
+///
+/// > split([6, 7, 8, 9], 2)
+/// #([6, 7], [8, 9])
+///
+/// > split([6, 7, 8, 9], 4)
+/// #([6, 7, 8, 9], [])
+///
+pub fn split(list list: List(a), at index: Int) -> #(List(a), List(a)) {
+ do_split(list, index, [])
+}
- fn do_pop(haystack, predicate, checked) {
- case haystack {
- [] -> Error(Nil)
- [x, ..rest] ->
- case predicate(x) {
- True -> Ok(#(x, append(reverse(checked), rest)))
- False -> do_pop(rest, predicate, [x, ..checked])
- }
- }
+fn do_split_while(
+ list: List(a),
+ f: fn(a) -> Bool,
+ acc: List(a),
+) -> #(List(a), List(a)) {
+ case list {
+ [] -> #(reverse(acc), [])
+ [x, ..xs] ->
+ case f(x) {
+ False -> #(reverse(acc), list)
+ _ -> do_split_while(xs, f, [x, ..acc])
+ }
}
+}
- /// Removes the first element in a given list for which the predicate funtion returns `True`.
- ///
- /// Returns `Error(Nil)` if no the function does not return True for any of the
- /// elements.
- ///
- /// ## Examples
- ///
- /// > pop([1, 2, 3], fn(x) { x > 2 })
- /// Ok(#(3, [1, 2]))
- ///
- /// > pop([1, 2, 3], fn(x) { x > 4 })
- /// Error(Nil)
- ///
- /// > pop([], fn(x) { True })
- /// Error(Nil)
- ///
- pub fn pop(
- in haystack: List(a),
- one_that is_desired: fn(a) -> Bool,
- ) -> Result(#(a, List(a)), Nil) {
- do_pop(haystack, is_desired, [])
- }
+/// Splits 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 })
+/// #([1, 2, 3], [4, 5])
+///
+/// > split_while([1, 2, 3, 4, 5], fn(x) { x <= 5 })
+/// #([1, 2, 3, 4, 5], [])
+///
+pub fn split_while(
+ list list: List(a),
+ satisfying predicate: fn(a) -> Bool,
+) -> #(List(a), List(a)) {
+ do_split_while(list, predicate, [])
+}
- fn do_pop_map(haystack, mapper, checked) {
- case haystack {
- [] -> Error(Nil)
- [x, ..rest] ->
- case mapper(x) {
- Ok(y) -> Ok(#(y, append(reverse(checked), rest)))
- Error(_) -> do_pop_map(rest, mapper, [x, ..checked])
- }
- }
- }
+/// Given a list of 2 element tuples, finds the first tuple that has a given
+/// key as the first element and returns 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([#("a", 0), #("b", 1)], "a")
+/// Ok(0)
+///
+/// > key_find([#("a", 0), #("b", 1)], "b")
+/// Ok(1)
+///
+/// > key_find([#("a", 0), #("b", 1)], "c")
+/// Error(Nil)
+///
+pub fn key_find(
+ in keyword_list: List(#(k, v)),
+ find desired_key: k,
+) -> Result(v, Nil) {
+ find_map(
+ keyword_list,
+ fn(keyword) {
+ let #(key, value) = keyword
+ case key == desired_key {
+ True -> Ok(value)
+ False -> Error(Nil)
+ }
+ },
+ )
+}
- /// Removes the first element in a given list for which the given function returns
- /// `Ok(new_value)` and return the new value as well as list with the value removed.
- ///
- /// Returns `Error(Nil)` if no the function does not return Ok for any of the
- /// elements.
- ///
- /// ## Examples
- ///
- /// > pop_map([[], [2], [3]], head)
- /// Ok(#(2, [[], [3]]))
- ///
- /// > pop_map([[], []], head)
- /// Error(Nil)
- ///
- /// > pop_map([], head)
- /// Error(Nil)
- ///
- pub fn pop_map(
- in haystack: List(a),
- one_that is_desired: fn(a) -> Result(b, c),
- ) -> Result(#(b, List(a)), Nil) {
- do_pop_map(haystack, is_desired, [])
+fn do_pop(haystack, predicate, checked) {
+ case haystack {
+ [] -> Error(Nil)
+ [x, ..rest] ->
+ case predicate(x) {
+ True -> Ok(#(x, append(reverse(checked), rest)))
+ False -> do_pop(rest, predicate, [x, ..checked])
+ }
}
+}
- /// Given a list of 2 element tuples, finds the first tuple that has a given
- /// key as the first element. This function will return the second element
- /// of the found tuple and list with tuple removed.
- ///
- /// If no tuple is found with the given key then `Error(Nil)` is returned.
- ///
- /// ## Examples
- ///
- /// > key_pop([#("a", 0), #("b", 1)], "a")
- /// Ok(#(0, [#("b", 1)])
- ///
- /// > key_pop([#("a", 0), #("b", 1)], "b")
- /// Ok(#(1, [#("a", 0)])
- ///
- /// > key_pop([#("a", 0), #("b", 1)], "c")
- /// Error(Nil)
- ///
- pub fn key_pop(
- haystack: List(#(k, v)),
- key: k,
- ) -> Result(#(v, List(#(k, v))), Nil) {
- pop_map(
- haystack,
- fn(entry) {
- let #(k, v) = entry
- case k {
- k if k == key -> Ok(v)
- _ -> Error(Nil)
- }
- },
- )
- }
+/// Removes the first element in a given list for which the predicate funtion returns `True`.
+///
+/// Returns `Error(Nil)` if no the function does not return True for any of the
+/// elements.
+///
+/// ## Examples
+///
+/// > pop([1, 2, 3], fn(x) { x > 2 })
+/// Ok(#(3, [1, 2]))
+///
+/// > pop([1, 2, 3], fn(x) { x > 4 })
+/// Error(Nil)
+///
+/// > pop([], fn(x) { True })
+/// Error(Nil)
+///
+pub fn pop(
+ in haystack: List(a),
+ one_that is_desired: fn(a) -> Bool,
+) -> Result(#(a, List(a)), Nil) {
+ do_pop(haystack, is_desired, [])
+}
- /// Given a list of 2 element tuples, inserts a key and value into the list.
- ///
- /// If there was already a tuple with the key then it is replaced, otherwise it
- /// is added to the end of the list.
- ///
- ///
- /// ## Examples
- ///
- /// > key_set([#(5, 0), #(4, 1)], 4, 100)
- /// [#(5, 0), #(4, 100)]
- ///
- /// > key_set([#(5, 0), #(4, 1)], 1, 100)
- /// [#(5, 0), #(4, 1), #(1, 100)]
- ///
- pub fn key_set(list: List(#(a, b)), key: a, value: b) -> List(#(a, b)) {
- case list {
- [] -> [#(key, value)]
- [#(k, _), ..rest] if k == key -> [#(key, value), ..rest]
- [first, ..rest] -> [first, ..key_set(rest, key, value)]
- }
+fn do_pop_map(haystack, mapper, checked) {
+ case haystack {
+ [] -> Error(Nil)
+ [x, ..rest] ->
+ case mapper(x) {
+ Ok(y) -> Ok(#(y, append(reverse(checked), rest)))
+ Error(_) -> do_pop_map(rest, mapper, [x, ..checked])
+ }
}
+}
- /// Calls a function for each element in a list, discarding the results.
- ///
- pub fn each(list: List(a), f: fn(a) -> b) -> Nil {
- case list {
- [] -> Nil
- [x, ..xs] -> {
- f(x)
- each(xs, f)
+/// Removes the first element in a given list for which the given function returns
+/// `Ok(new_value)` and return the new value as well as list with the value removed.
+///
+/// Returns `Error(Nil)` if no the function does not return Ok for any of the
+/// elements.
+///
+/// ## Examples
+///
+/// > pop_map([[], [2], [3]], head)
+/// Ok(#(2, [[], [3]]))
+///
+/// > pop_map([[], []], head)
+/// Error(Nil)
+///
+/// > pop_map([], head)
+/// Error(Nil)
+///
+pub fn pop_map(
+ in haystack: List(a),
+ one_that is_desired: fn(a) -> Result(b, c),
+) -> Result(#(b, List(a)), Nil) {
+ do_pop_map(haystack, is_desired, [])
+}
+
+/// Given a list of 2 element tuples, finds the first tuple that has a given
+/// key as the first element. This function will return the second element
+/// of the found tuple and list with tuple removed.
+///
+/// If no tuple is found with the given key then `Error(Nil)` is returned.
+///
+/// ## Examples
+///
+/// > key_pop([#("a", 0), #("b", 1)], "a")
+/// Ok(#(0, [#("b", 1)])
+///
+/// > key_pop([#("a", 0), #("b", 1)], "b")
+/// Ok(#(1, [#("a", 0)])
+///
+/// > key_pop([#("a", 0), #("b", 1)], "c")
+/// Error(Nil)
+///
+pub fn key_pop(
+ haystack: List(#(k, v)),
+ key: k,
+) -> Result(#(v, List(#(k, v))), Nil) {
+ pop_map(
+ haystack,
+ fn(entry) {
+ let #(k, v) = entry
+ case k {
+ k if k == key -> Ok(v)
+ _ -> Error(Nil)
}
- }
+ },
+ )
+}
+
+/// Given a list of 2 element tuples, inserts a key and value into the list.
+///
+/// If there was already a tuple with the key then it is replaced, otherwise it
+/// is added to the end of the list.
+///
+///
+/// ## Examples
+///
+/// > key_set([#(5, 0), #(4, 1)], 4, 100)
+/// [#(5, 0), #(4, 100)]
+///
+/// > key_set([#(5, 0), #(4, 1)], 1, 100)
+/// [#(5, 0), #(4, 1), #(1, 100)]
+///
+pub fn key_set(list: List(#(a, b)), key: a, value: b) -> List(#(a, b)) {
+ case list {
+ [] -> [#(key, value)]
+ [#(k, _), ..rest] if k == key -> [#(key, value), ..rest]
+ [first, ..rest] -> [first, ..key_set(rest, key, value)]
}
+}
- fn do_partition(list, categorise, trues, falses) {
- case list {
- [] -> #(reverse(trues), reverse(falses))
- [x, ..xs] ->
- case categorise(x) {
- True -> do_partition(xs, categorise, [x, ..trues], falses)
- False -> do_partition(xs, categorise, trues, [x, ..falses])
- }
+/// Calls a function for each element in a list, discarding the results.
+///
+pub fn each(list: List(a), f: fn(a) -> b) -> Nil {
+ case list {
+ [] -> Nil
+ [x, ..xs] -> {
+ f(x)
+ each(xs, f)
}
}
+}
- pub fn partition(
- list: List(a),
- with categorise: fn(a) -> Bool,
- ) -> #(List(a), List(a)) {
- do_partition(list, categorise, [], [])
+fn do_partition(list, categorise, trues, falses) {
+ case list {
+ [] -> #(reverse(trues), reverse(falses))
+ [x, ..xs] ->
+ case categorise(x) {
+ True -> do_partition(xs, categorise, [x, ..trues], falses)
+ False -> do_partition(xs, categorise, trues, [x, ..falses])
+ }
}
+}
- /// Returns all the permutations of a list
- /// All values must be unique
- ///
- /// ## Examples
- ///
- /// > permutations([1, 2])
- /// [[1, 2], [2, 1]]
- ///
- pub fn permutations(l: List(a)) -> List(List(a)) {
- case l {
- [] -> [[]]
- _ ->
- map(
- l,
- fn(x) {
- filter(l, fn(y) { y != x })
- |> permutations
- |> map(append([x], _))
- },
- )
- |> flatten
- }
+pub fn partition(
+ list: List(a),
+ with categorise: fn(a) -> Bool,
+) -> #(List(a), List(a)) {
+ do_partition(list, categorise, [], [])
+}
+
+/// Returns all the permutations of a list
+/// All values must be unique
+///
+/// ## Examples
+///
+/// > permutations([1, 2])
+/// [[1, 2], [2, 1]]
+///
+pub fn permutations(l: List(a)) -> List(List(a)) {
+ case l {
+ [] -> [[]]
+ _ ->
+ map(
+ l,
+ fn(x) {
+ filter(l, fn(y) { y != x })
+ |> permutations
+ |> map(append([x], _))
+ },
+ )
+ |> flatten
}
+}
- fn do_window(acc: List(List(a)), l: List(a), n: Int) -> List(List(a)) {
- let window = take(l, n)
+fn do_window(acc: List(List(a)), l: List(a), n: Int) -> List(List(a)) {
+ let window = take(l, n)
- case length(window) == n {
- True -> do_window([window, ..acc], drop(l, 1), n)
- False -> acc
- }
+ case length(window) == n {
+ True -> do_window([window, ..acc], drop(l, 1), n)
+ False -> acc
}
+}
- /// Returns a list of sliding window
- ///
- /// ## Examples
- ///
- /// ```
- /// > window([1,2,3,4,5], 3)
- /// [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
- ///
- /// > window([1, 2], 4)
- /// []
- /// ```
- ///
- pub fn window(l: List(a), by n: Int) -> List(List(a)) {
- do_window([], l, n)
- |> reverse
- }
+/// Returns a list of sliding window
+///
+/// ## Examples
+///
+/// ```
+/// > window([1,2,3,4,5], 3)
+/// [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
+///
+/// > window([1, 2], 4)
+/// []
+/// ```
+///
+pub fn window(l: List(a), by n: Int) -> List(List(a)) {
+ do_window([], l, n)
+ |> reverse
+}
- /// Returns a list of tuples containing two contiguous elements
- ///
- /// ## Examples
- ///
- /// ```
- /// > window_by_2([1,2,3,4])
- /// [#(1, 2), #(2, 3), #(3, 4)]
- ///
- /// > window_by_2([1])
- /// []
- /// ```
- ///
- pub fn window_by_2(l: List(a)) -> List(#(a, a)) {
- zip(l, drop(l, 1))
- }
+/// Returns a list of tuples containing two contiguous elements
+///
+/// ## Examples
+///
+/// ```
+/// > window_by_2([1,2,3,4])
+/// [#(1, 2), #(2, 3), #(3, 4)]
+///
+/// > window_by_2([1])
+/// []
+/// ```
+///
+pub fn window_by_2(l: List(a)) -> List(#(a, a)) {
+ zip(l, drop(l, 1))
+}
- /// Drops the first elements in a given list for which the predicate funtion returns `True`.
- ///
- /// ## Examples
- ///
- /// > drop_while([1, 2, 3, 4], fun (x) { x < 3 })
- /// [3, 4]
- ///
- pub fn drop_while(
- in list: List(a),
- satisfying predicate: fn(a) -> Bool,
- ) -> List(a) {
- case list {
- [] -> []
- [x, ..xs] ->
- case predicate(x) {
- True -> drop_while(xs, predicate)
- False -> [x, ..xs]
- }
- }
+/// Drops the first elements in a given list for which the predicate funtion returns `True`.
+///
+/// ## Examples
+///
+/// > drop_while([1, 2, 3, 4], fun (x) { x < 3 })
+/// [3, 4]
+///
+pub fn drop_while(
+ in list: List(a),
+ satisfying predicate: fn(a) -> Bool,
+) -> List(a) {
+ case list {
+ [] -> []
+ [x, ..xs] ->
+ case predicate(x) {
+ True -> drop_while(xs, predicate)
+ False -> [x, ..xs]
+ }
}
+}
- fn do_take_while(
- list: List(a),
- predicate: fn(a) -> Bool,
- acc: List(a),
- ) -> List(a) {
- case list {
- [] -> reverse(acc)
- [head, ..tail] ->
- case predicate(head) {
- True -> do_take_while(tail, predicate, [head, ..acc])
- False -> reverse(acc)
- }
- }
+fn do_take_while(
+ list: List(a),
+ predicate: fn(a) -> Bool,
+ acc: List(a),
+) -> List(a) {
+ case list {
+ [] -> reverse(acc)
+ [head, ..tail] ->
+ case predicate(head) {
+ True -> do_take_while(tail, predicate, [head, ..acc])
+ False -> reverse(acc)
+ }
}
+}
- /// Takes the first elements in a given list for which the predicate funtion returns `True`.
- ///
- /// ## Examples
- ///
- /// > take_while([1, 2, 3, 2, 4], fun (x) { x < 3 })
- /// [1, 2]
- ///
- pub fn take_while(
- in list: List(a),
- satisfying predicate: fn(a) -> Bool,
- ) -> List(a) {
- do_take_while(list, predicate, [])
- }
+/// Takes the first elements in a given list for which the predicate funtion returns `True`.
+///
+/// ## Examples
+///
+/// > take_while([1, 2, 3, 2, 4], fun (x) { x < 3 })
+/// [1, 2]
+///
+pub fn take_while(
+ in list: List(a),
+ satisfying predicate: fn(a) -> Bool,
+) -> List(a) {
+ do_take_while(list, predicate, [])
+}
- fn do_chunk(
- list: List(a),
- f: fn(a) -> key,
- previous_key: key,
- current_chunk: List(a),
- acc: List(List(a)),
- ) -> List(List(a)) {
- case list {
- [] -> reverse([reverse(current_chunk), ..acc])
- [head, ..tail] -> {
- let key = f(head)
- case key == previous_key {
- False ->
- do_chunk(tail, f, key, [head], [reverse(current_chunk), ..acc])
- True -> do_chunk(tail, f, key, [head, ..current_chunk], acc)
- }
+fn do_chunk(
+ list: List(a),
+ f: fn(a) -> key,
+ previous_key: key,
+ current_chunk: List(a),
+ acc: List(List(a)),
+) -> List(List(a)) {
+ case list {
+ [] -> reverse([reverse(current_chunk), ..acc])
+ [head, ..tail] -> {
+ let key = f(head)
+ case key == previous_key {
+ False -> do_chunk(tail, f, key, [head], [reverse(current_chunk), ..acc])
+ True -> do_chunk(tail, f, key, [head, ..current_chunk], acc)
}
}
}
+}
- /// Returns a list of chunks in which
- /// the result of calling `f` on each element is the same.
- ///
- /// ## Examples
- ///
- /// > [1, 2, 2, 3, 4, 4, 6, 7, 7] |> chunk(by: fn(n) { n % 2 })
- /// [[1], [2, 2], [3], [4, 4, 6], [7, 7]]
- ///
- pub fn chunk(in list: List(a), by f: fn(a) -> key) -> List(List(a)) {
- case list {
- [] -> []
- [head, ..tail] -> do_chunk(tail, f, f(head), [head], [])
- }
+/// Returns a list of chunks in which
+/// the result of calling `f` on each element is the same.
+///
+/// ## Examples
+///
+/// > [1, 2, 2, 3, 4, 4, 6, 7, 7] |> chunk(by: fn(n) { n % 2 })
+/// [[1], [2, 2], [3], [4, 4, 6], [7, 7]]
+///
+pub fn chunk(in list: List(a), by f: fn(a) -> key) -> List(List(a)) {
+ case list {
+ [] -> []
+ [head, ..tail] -> do_chunk(tail, f, f(head), [head], [])
}
+}
- fn do_sized_chunk(
- list: List(a),
- count: Int,
- left: Int,
- current_chunk: List(a),
- acc: List(List(a)),
- ) -> List(List(a)) {
- case list {
- [] ->
- case current_chunk {
- [] -> reverse(acc)
- remaining -> reverse([reverse(remaining), ..acc])
- }
- [head, ..tail] -> {
- let chunk = [head, ..current_chunk]
- case left > 1 {
- False ->
- do_sized_chunk(tail, count, count, [], [reverse(chunk), ..acc])
- True -> do_sized_chunk(tail, count, left - 1, chunk, acc)
- }
+fn do_sized_chunk(
+ list: List(a),
+ count: Int,
+ left: Int,
+ current_chunk: List(a),
+ acc: List(List(a)),
+) -> List(List(a)) {
+ case list {
+ [] ->
+ case current_chunk {
+ [] -> reverse(acc)
+ remaining -> reverse([reverse(remaining), ..acc])
+ }
+ [head, ..tail] -> {
+ let chunk = [head, ..current_chunk]
+ case left > 1 {
+ False -> do_sized_chunk(tail, count, count, [], [reverse(chunk), ..acc])
+ True -> do_sized_chunk(tail, count, left - 1, chunk, acc)
}
}
}
+}
- /// Returns a list of chunks containing `count` elements each.
- ///
- /// If the last chunk does not have `count` elements, it is instead
- /// a partial chunk, with less than `count` elements.
- ///
- /// For any `count` less than 1 this function behaves as if it was set to 1.
- ///
- /// ## Examples
- ///
- /// > [1, 2, 3, 4, 5, 6] |> chunk(into: 2)
- /// [[1, 2], [3, 4], [5, 6]]
- ///
- /// > [1, 2, 3, 4, 5, 6, 7, 8] |> chunk(into: 3)
- /// [[1, 2, 3], [4, 5, 6], [7, 8]]
- ///
- pub fn sized_chunk(in list: List(a), into count: Int) -> List(List(a)) {
- do_sized_chunk(list, count, count, [], [])
- }
+/// Returns a list of chunks containing `count` elements each.
+///
+/// If the last chunk does not have `count` elements, it is instead
+/// a partial chunk, with less than `count` elements.
+///
+/// For any `count` less than 1 this function behaves as if it was set to 1.
+///
+/// ## Examples
+///
+/// > [1, 2, 3, 4, 5, 6] |> chunk(into: 2)
+/// [[1, 2], [3, 4], [5, 6]]
+///
+/// > [1, 2, 3, 4, 5, 6, 7, 8] |> chunk(into: 3)
+/// [[1, 2, 3], [4, 5, 6], [7, 8]]
+///
+pub fn sized_chunk(in list: List(a), into count: Int) -> List(List(a)) {
+ do_sized_chunk(list, count, count, [], [])
+}
- /// This function acts similar to fold, but does not take an initial state.
- /// Instead, it starts from the first element in the list
- /// and combines it with each subsequent element in turn using the given function.
- /// The function is called as fun(current_element, accumulator).
- ///
- /// Returns `Ok` to indicate a successful run, and `Error` if called on an empty list.
- ///
- /// ## Examples
- ///
- /// > [] |> reduce(fn(x, y) { x + y })
- /// Error(Nil)
- ///
- /// > [1, 2, 3, 4, 5] |> reduce(fn(x, y) { x + y })
- /// Ok(15)
- ///
- pub fn reduce(over list: List(a), with fun: fn(a, a) -> a) -> Result(a, Nil) {
- case list {
- [] -> Error(Nil)
- [head, ..tail] ->
- fold(tail, head, fun)
- |> Ok
- }
+/// This function acts similar to fold, but does not take an initial state.
+/// Instead, it starts from the first element in the list
+/// and combines it with each subsequent element in turn using the given function.
+/// The function is called as fun(current_element, accumulator).
+///
+/// Returns `Ok` to indicate a successful run, and `Error` if called on an empty list.
+///
+/// ## Examples
+///
+/// > [] |> reduce(fn(x, y) { x + y })
+/// Error(Nil)
+///
+/// > [1, 2, 3, 4, 5] |> reduce(fn(x, y) { x + y })
+/// Ok(15)
+///
+pub fn reduce(over list: List(a), with fun: fn(a, a) -> a) -> Result(a, Nil) {
+ case list {
+ [] -> Error(Nil)
+ [head, ..tail] ->
+ fold(tail, head, fun)
+ |> Ok
}
+}
- fn do_scan(
- list: List(a),
- accumulator: b,
- accumulated: List(b),
- fun: fn(a, b) -> b,
- ) -> List(b) {
- case list {
- [] -> reverse(accumulated)
- [x, ..xs] -> {
- let next = fun(x, accumulator)
- do_scan(xs, next, [next, ..accumulated], fun)
- }
+fn do_scan(
+ list: List(a),
+ accumulator: b,
+ accumulated: List(b),
+ fun: fn(a, b) -> b,
+) -> List(b) {
+ case list {
+ [] -> reverse(accumulated)
+ [x, ..xs] -> {
+ let next = fun(x, accumulator)
+ do_scan(xs, next, [next, ..accumulated], fun)
}
}
+}
- /// Similar to `fold`, but yields the state of the accumulator at each stage.
- ///
- /// ## Examples
- ///
- /// > scan(over: [1, 2, 3], from: 100, with: fn(i, acc) { acc + i })
- /// [101, 103, 106]
- ///
- pub fn scan(
- over list: List(a),
- from initial: b,
- with fun: fn(a, b) -> b,
- ) -> List(b) {
- do_scan(list, initial, [], fun)
- }
+/// Similar to `fold`, but yields the state of the accumulator at each stage.
+///
+/// ## Examples
+///
+/// > scan(over: [1, 2, 3], from: 100, with: fn(i, acc) { acc + i })
+/// [101, 103, 106]
+///
+pub fn scan(
+ over list: List(a),
+ from initial: b,
+ with fun: fn(a, b) -> b,
+) -> List(b) {
+ do_scan(list, initial, [], fun)
+}
- /// Returns the last element in the given list.
- ///
- /// Returns `Error(Nil)` if the list is empty.
- ///
- /// This function runs in linear time.
- /// For a collection oriented around performant access at either end,
- /// see `gleam/queue.Queue`.
- ///
- /// ## Examples
- ///
- /// > last([])
- /// Error(Nil)
- ///
- /// > last([1, 2, 3, 4, 5])
- /// Ok(5)
- ///
- pub fn last(list: List(a)) -> Result(a, Nil) {
- list
- |> reduce(fn(elem, _) { elem })
- }
+/// Returns the last element in the given list.
+///
+/// Returns `Error(Nil)` if the list is empty.
+///
+/// This function runs in linear time.
+/// For a collection oriented around performant access at either end,
+/// see `gleam/queue.Queue`.
+///
+/// ## Examples
+///
+/// > last([])
+/// Error(Nil)
+///
+/// > last([1, 2, 3, 4, 5])
+/// Ok(5)
+///
+pub fn last(list: List(a)) -> Result(a, Nil) {
+ list
+ |> reduce(fn(elem, _) { elem })
+}
- /// Return unique combinations of elements in the list
- ///
- /// ## Examples
- ///
- /// ```
- /// > combinations([1, 2, 3], 2)
- /// [[1, 2], [1, 3], [2, 3]]
- ///
- /// > combinations([1, 2, 3, 4], 3)
- /// [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]
- /// ```
- ///
- pub fn combinations(items: List(a), by n: Int) -> List(List(a)) {
- case n {
- 0 -> [[]]
- _ ->
- case items {
- [] -> []
- [x, ..xs] -> {
- let first_combinations =
- map(combinations(xs, n - 1), with: fn(com) { [x, ..com] })
- |> reverse
- fold(
- first_combinations,
- combinations(xs, n),
- fn(c, acc) { [c, ..acc] },
- )
- }
+/// Return unique combinations of elements in the list
+///
+/// ## Examples
+///
+/// ```
+/// > combinations([1, 2, 3], 2)
+/// [[1, 2], [1, 3], [2, 3]]
+///
+/// > combinations([1, 2, 3, 4], 3)
+/// [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]
+/// ```
+///
+pub fn combinations(items: List(a), by n: Int) -> List(List(a)) {
+ case n {
+ 0 -> [[]]
+ _ ->
+ case items {
+ [] -> []
+ [x, ..xs] -> {
+ let first_combinations =
+ map(combinations(xs, n - 1), with: fn(com) { [x, ..com] })
+ |> reverse
+ fold(
+ first_combinations,
+ combinations(xs, n),
+ fn(c, acc) { [c, ..acc] },
+ )
}
- }
+ }
}
+}
- fn do_combination_pairs(items: List(a)) -> List(List(#(a, a))) {
- case items {
- [] -> []
- [x, ..xs] -> {
- let first_combinations = map(xs, with: fn(other) { #(x, other) })
- [first_combinations, ..do_combination_pairs(xs)]
- }
+fn do_combination_pairs(items: List(a)) -> List(List(#(a, a))) {
+ case items {
+ [] -> []
+ [x, ..xs] -> {
+ let first_combinations = map(xs, with: fn(other) { #(x, other) })
+ [first_combinations, ..do_combination_pairs(xs)]
}
}
+}
- /// Return unique pair combinations of elements in the list
- ///
- /// ## Examples
- ///
- /// ```
- /// > combination_pairs([1, 2, 3])
- /// [#(1, 2), #(1, 3), #(2, 3)]
- /// ```
- ///
- pub fn combination_pairs(items: List(a)) -> List(#(a, a)) {
- do_combination_pairs(items)
- |> flatten
- }
+/// Return unique pair combinations of elements in the list
+///
+/// ## Examples
+///
+/// ```
+/// > combination_pairs([1, 2, 3])
+/// [#(1, 2), #(1, 3), #(2, 3)]
+/// ```
+///
+pub fn combination_pairs(items: List(a)) -> List(#(a, a)) {
+ do_combination_pairs(items)
+ |> flatten
+}
- /// Make a list alternating the elements from the given lists
- ///
- /// ## Examples
- ///
- /// ```
- /// > list.interleave([[1, 2], [101, 102], [201, 202]])
- /// [1, 101, 201, 2, 102, 202]
- /// ```
- ///
- pub fn interleave(list: List(List(a))) -> List(a) {
- transpose(list)
- |> flatten
- }
+/// Make a list alternating the elements from the given lists
+///
+/// ## Examples
+///
+/// ```
+/// > list.interleave([[1, 2], [101, 102], [201, 202]])
+/// [1, 101, 201, 2, 102, 202]
+/// ```
+///
+pub fn interleave(list: List(List(a))) -> List(a) {
+ transpose(list)
+ |> flatten
+}
- /// Transpose rows and columns of the list of lists.
- ///
- /// ## Examples
- ///
- /// ```
- /// > transpose([[1, 2, 3], [101, 102, 103]])
- /// [[1, 101], [2, 102], [3, 103]]
- /// ```
- pub fn transpose(list_of_list: List(List(a))) -> List(List(a)) {
- let take_first = fn(list) {
- case list {
- [] -> []
- [f] -> [f]
- [f, .._rest] -> [f]
- }
+/// Transpose rows and columns of the list of lists.
+///
+/// ## Examples
+///
+/// ```
+/// > transpose([[1, 2, 3], [101, 102, 103]])
+/// [[1, 101], [2, 102], [3, 103]]
+/// ```
+pub fn transpose(list_of_list: List(List(a))) -> List(List(a)) {
+ let take_first = fn(list) {
+ case list {
+ [] -> []
+ [f] -> [f]
+ [f, .._rest] -> [f]
}
+ }
- case list_of_list {
- [] -> []
- [[], ..xss] -> transpose(xss)
- rows -> {
- let firsts =
- rows
- |> map(take_first)
- |> flatten
- let rest = transpose(map(rows, drop(_, 1)))
- [firsts, ..rest]
- }
+ case list_of_list {
+ [] -> []
+ [[], ..xss] -> transpose(xss)
+ rows -> {
+ let firsts =
+ rows
+ |> map(take_first)
+ |> flatten
+ let rest = transpose(map(rows, drop(_, 1)))
+ [firsts, ..rest]
}
}
}
diff --git a/test/gleam/list_test.gleam b/test/gleam/list_test.gleam
index bfe2c0b..ee33e88 100644
--- a/test/gleam/list_test.gleam
+++ b/test/gleam/list_test.gleam
@@ -1,726 +1,738 @@
-if erlang {
- import gleam/should
- import gleam/list
- import gleam/int
- import gleam/float
- import gleam/string
- import gleam/pair
-
- pub fn length_test() {
- list.length([])
- |> should.equal(0)
-
- list.length([1])
- |> should.equal(1)
-
- list.length([1, 1])
- |> should.equal(2)
-
- list.length([1, 1, 1])
- |> should.equal(3)
- }
+import gleam/should
+import gleam/list
+import gleam/int
+import gleam/float
+import gleam/string
+import gleam/pair
- pub fn reverse_test() {
- list.reverse([])
- |> should.equal([])
- list.reverse([1, 2, 3, 4, 5])
- |> should.equal([5, 4, 3, 2, 1])
- }
+pub fn length_test() {
+ list.length([])
+ |> should.equal(0)
- pub fn is_empty_test() {
- list.is_empty([])
- |> should.be_true
- list.is_empty([1])
- |> should.be_false
- }
+ list.length([1])
+ |> should.equal(1)
- pub fn contains_test() {
- list.contains([0, 4, 5, 1], 1)
- |> should.be_true
- list.contains([0, 4, 5, 7], 1)
- |> should.be_false
- list.contains([], 1)
- |> should.be_false
- }
+ list.length([1, 1])
+ |> should.equal(2)
- pub fn head_test() {
- list.head([0, 4, 5, 7])
- |> should.equal(Ok(0))
+ list.length([1, 1, 1])
+ |> should.equal(3)
+}
- list.head([])
- |> should.equal(Error(Nil))
- }
+pub fn reverse_test() {
+ list.reverse([])
+ |> should.equal([])
+ list.reverse([1, 2, 3, 4, 5])
+ |> should.equal([5, 4, 3, 2, 1])
+}
- pub fn tail_test() {
- list.tail([0, 4, 5, 7])
- |> should.equal(Ok([4, 5, 7]))
+pub fn is_empty_test() {
+ list.is_empty([])
+ |> should.be_true
+ list.is_empty([1])
+ |> should.be_false
+}
- list.tail([0])
- |> should.equal(Ok([]))
+pub fn contains_test() {
+ list.contains([0, 4, 5, 1], 1)
+ |> should.be_true
+ list.contains([0, 4, 5, 7], 1)
+ |> should.be_false
+ list.contains([], 1)
+ |> should.be_false
+}
- list.tail([])
- |> should.equal(Error(Nil))
- }
+pub fn head_test() {
+ list.head([0, 4, 5, 7])
+ |> should.equal(Ok(0))
- pub fn filter_test() {
- []
- |> list.filter(fn(_) { True })
- |> should.equal([])
+ list.head([])
+ |> should.equal(Error(Nil))
+}
- [0, 4, 5, 7, 3]
- |> list.filter(fn(_) { True })
- |> should.equal([0, 4, 5, 7, 3])
+pub fn tail_test() {
+ list.tail([0, 4, 5, 7])
+ |> should.equal(Ok([4, 5, 7]))
- [0, 4, 5, 7, 3]
- |> list.filter(fn(x) { x > 4 })
- |> should.equal([5, 7])
+ list.tail([0])
+ |> should.equal(Ok([]))
- [0, 4, 5, 7, 3]
- |> list.filter(fn(x) { x < 4 })
- |> should.equal([0, 3])
- }
+ list.tail([])
+ |> should.equal(Error(Nil))
+}
- pub fn filter_map_test() {
- [2, 4, 6, 1]
- |> list.filter_map(fn(x) { Ok(x + 1) })
- |> should.equal([3, 5, 7, 2])
+pub fn filter_test() {
+ []
+ |> list.filter(fn(_) { True })
+ |> should.equal([])
- [2, 4, 6, 1]
- |> list.filter_map(Error)
- |> should.equal([])
- }
+ [0, 4, 5, 7, 3]
+ |> list.filter(fn(_) { True })
+ |> should.equal([0, 4, 5, 7, 3])
- pub fn map_test() {
- []
- |> list.map(fn(x) { x * 2 })
- |> should.equal([])
+ [0, 4, 5, 7, 3]
+ |> list.filter(fn(x) { x > 4 })
+ |> should.equal([5, 7])
- [0, 4, 5, 7, 3]
- |> list.map(fn(x) { x * 2 })
- |> should.equal([0, 8, 10, 14, 6])
- }
+ [0, 4, 5, 7, 3]
+ |> list.filter(fn(x) { x < 4 })
+ |> should.equal([0, 3])
+}
- pub fn map_fold_test() {
- [1, 2, 3, 4]
- |> list.map_fold(from: 0, with: fn(i, acc) { #(i * 2, acc + i) })
- |> should.equal(#([2, 4, 6, 8], 10))
- }
+pub fn filter_map_test() {
+ [2, 4, 6, 1]
+ |> list.filter_map(fn(x) { Ok(x + 1) })
+ |> should.equal([3, 5, 7, 2])
- pub fn try_map_test() {
- let fun = fn(x) {
- case x == 6 || x == 5 || x == 4 {
- True -> Ok(x * 2)
- False -> Error(x)
- }
- }
+ [2, 4, 6, 1]
+ |> list.filter_map(Error)
+ |> should.equal([])
+}
- [5, 6, 5, 6]
- |> list.try_map(fun)
- |> should.equal(Ok([10, 12, 10, 12]))
+pub fn map_test() {
+ []
+ |> list.map(fn(x) { x * 2 })
+ |> should.equal([])
- [4, 6, 5, 7, 3]
- |> list.try_map(fun)
- |> should.equal(Error(7))
- }
+ [0, 4, 5, 7, 3]
+ |> list.map(fn(x) { x * 2 })
+ |> should.equal([0, 8, 10, 14, 6])
+}
- pub fn drop_test() {
- []
- |> list.drop(5)
- |> should.equal([])
+pub fn map_fold_test() {
+ [1, 2, 3, 4]
+ |> list.map_fold(from: 0, with: fn(i, acc) { #(i * 2, acc + i) })
+ |> should.equal(#([2, 4, 6, 8], 10))
+}
- [1, 2, 3, 4, 5, 6, 7, 8]
- |> list.drop(5)
- |> should.equal([6, 7, 8])
+pub fn try_map_test() {
+ let fun = fn(x) {
+ case x == 6 || x == 5 || x == 4 {
+ True -> Ok(x * 2)
+ False -> Error(x)
+ }
}
- pub fn take_test() {
- []
- |> list.take(5)
- |> should.equal([])
- [1, 2, 3, 4, 5, 6, 7, 8]
- |> list.take(5)
- |> should.equal([1, 2, 3, 4, 5])
- }
+ [5, 6, 5, 6]
+ |> list.try_map(fun)
+ |> should.equal(Ok([10, 12, 10, 12]))
- pub fn new_test() {
- list.new()
- |> should.equal([])
- }
+ [4, 6, 5, 7, 3]
+ |> list.try_map(fun)
+ |> should.equal(Error(7))
+}
- pub fn append_test() {
- list.append([1], [2, 3])
- |> should.equal([1, 2, 3])
- }
+pub fn drop_test() {
+ []
+ |> list.drop(5)
+ |> should.equal([])
- pub fn flatten_test() {
- list.flatten([])
- |> should.equal([])
+ [1, 2, 3, 4, 5, 6, 7, 8]
+ |> list.drop(5)
+ |> should.equal([6, 7, 8])
+}
- list.flatten([[]])
- |> should.equal([])
+pub fn take_test() {
+ []
+ |> list.take(5)
+ |> should.equal([])
+ [1, 2, 3, 4, 5, 6, 7, 8]
+ |> list.take(5)
+ |> should.equal([1, 2, 3, 4, 5])
+}
- list.flatten([[], [], []])
- |> should.equal([])
+pub fn new_test() {
+ list.new()
+ |> should.equal([])
+}
- list.flatten([[1, 2], [], [3, 4]])
- |> should.equal([1, 2, 3, 4])
- }
+pub fn append_test() {
+ list.append([1], [2, 3])
+ |> should.equal([1, 2, 3])
+}
- pub fn flat_map_test() {
- list.flat_map([1, 10, 20], fn(x) { [x, x + 1] })
- |> should.equal([1, 2, 10, 11, 20, 21])
- }
+pub fn flatten_test() {
+ list.flatten([])
+ |> should.equal([])
- pub fn fold_test() {
- [1, 2, 3]
- |> list.fold([], fn(x, acc) { [x, ..acc] })
- |> should.equal([3, 2, 1])
- }
+ list.flatten([[]])
+ |> should.equal([])
- pub fn fold_right_test() {
- [1, 2, 3]
- |> list.fold_right(from: [], with: fn(x, acc) { [x, ..acc] })
- |> should.equal([1, 2, 3])
- }
+ list.flatten([[], [], []])
+ |> should.equal([])
- pub fn index_fold_test() {
- ["a", "b", "c"]
- |> list.index_fold([], fn(ix, i, acc) { [#(ix, i), ..acc] })
- |> should.equal([#(2, "c"), #(1, "b"), #(0, "a")])
- }
+ list.flatten([[1, 2], [], [3, 4]])
+ |> should.equal([1, 2, 3, 4])
+}
- pub fn fold_until_test() {
- [1, 2, 3, 4]
- |> list.fold_until(
- from: 0,
- with: fn(n, acc) {
- case n < 4 {
- True -> list.Continue(acc + n)
- False -> list.Stop(acc)
- }
- },
- )
- |> should.equal(6)
- }
+pub fn flat_map_test() {
+ list.flat_map([1, 10, 20], fn(x) { [x, x + 1] })
+ |> should.equal([1, 2, 10, 11, 20, 21])
+}
- pub fn try_fold_test() {
- [1, 2, 3]
- |> list.try_fold(
- 0,
- fn(i, acc) {
- case i < 4 {
- True -> Ok(acc + i)
- False -> Error(Nil)
- }
- },
- )
- |> should.equal(Ok(6))
-
- [1, 2, 3]
- |> list.try_fold(
- 0,
- fn(i, acc) {
- case i < 3 {
- True -> Ok(acc + i)
- False -> Error(Nil)
- }
- },
- )
- |> should.equal(Error(Nil))
- }
+pub fn fold_test() {
+ [1, 2, 3]
+ |> list.fold([], fn(x, acc) { [x, ..acc] })
+ |> should.equal([3, 2, 1])
+}
- pub fn find_map_test() {
- let f = fn(x) {
- case x {
- 2 -> Ok(4)
- _ -> Error(Nil)
- }
- }
+pub fn fold_right_test() {
+ [1, 2, 3]
+ |> list.fold_right(from: [], with: fn(x, acc) { [x, ..acc] })
+ |> should.equal([1, 2, 3])
+}
+
+pub fn index_fold_test() {
+ ["a", "b", "c"]
+ |> list.index_fold([], fn(ix, i, acc) { [#(ix, i), ..acc] })
+ |> should.equal([#(2, "c"), #(1, "b"), #(0, "a")])
+}
- [1, 2, 3]
- |> list.find_map(with: f)
- |> should.equal(Ok(4))
+pub fn fold_until_test() {
+ [1, 2, 3, 4]
+ |> list.fold_until(
+ from: 0,
+ with: fn(n, acc) {
+ case n < 4 {
+ True -> list.Continue(acc + n)
+ False -> list.Stop(acc)
+ }
+ },
+ )
+ |> should.equal(6)
+}
- [1, 3, 2]
- |> list.find_map(with: f)
- |> should.equal(Ok(4))
+pub fn try_fold_test() {
+ [1, 2, 3]
+ |> list.try_fold(
+ 0,
+ fn(i, acc) {
+ case i < 4 {
+ True -> Ok(acc + i)
+ False -> Error(Nil)
+ }
+ },
+ )
+ |> should.equal(Ok(6))
+
+ [1, 2, 3]
+ |> list.try_fold(
+ 0,
+ fn(i, acc) {
+ case i < 3 {
+ True -> Ok(acc + i)
+ False -> Error(Nil)
+ }
+ },
+ )
+ |> should.equal(Error(Nil))
+}
- [1, 3]
- |> list.find_map(with: f)
- |> should.equal(Error(Nil))
+pub fn find_map_test() {
+ let f = fn(x) {
+ case x {
+ 2 -> Ok(4)
+ _ -> Error(Nil)
+ }
}
- pub fn find_test() {
- let is_two = fn(x) { x == 2 }
+ [1, 2, 3]
+ |> list.find_map(with: f)
+ |> should.equal(Ok(4))
- [1, 2, 3]
- |> list.find(one_that: is_two)
- |> should.equal(Ok(2))
+ [1, 3, 2]
+ |> list.find_map(with: f)
+ |> should.equal(Ok(4))
- [1, 3, 2]
- |> list.find(one_that: is_two)
- |> should.equal(Ok(2))
+ [1, 3]
+ |> list.find_map(with: f)
+ |> should.equal(Error(Nil))
+}
- [1, 3]
- |> list.find(one_that: is_two)
- |> should.equal(Error(Nil))
- }
+pub fn find_test() {
+ let is_two = fn(x) { x == 2 }
- pub fn all_test() {
- list.all([1, 2, 3, 4, 5], fn(x) { x > 0 })
- |> should.equal(True)
+ [1, 2, 3]
+ |> list.find(one_that: is_two)
+ |> should.equal(Ok(2))
- list.all([1, 2, 3, 4, 5], fn(x) { x < 0 })
- |> should.equal(False)
+ [1, 3, 2]
+ |> list.find(one_that: is_two)
+ |> should.equal(Ok(2))
- list.all([], fn(_) { False })
- |> should.equal(True)
- }
+ [1, 3]
+ |> list.find(one_that: is_two)
+ |> should.equal(Error(Nil))
+}
- pub fn any_test() {
- list.any([1, 2, 3, 4, 5], fn(x) { x == 2 })
- |> should.equal(True)
+pub fn all_test() {
+ list.all([1, 2, 3, 4, 5], fn(x) { x > 0 })
+ |> should.equal(True)
- list.any([1, 2, 3, 4, 5], fn(x) { x < 0 })
- |> should.equal(False)
+ list.all([1, 2, 3, 4, 5], fn(x) { x < 0 })
+ |> should.equal(False)
- list.any([], fn(_) { False })
- |> should.equal(False)
- }
+ list.all([], fn(_) { False })
+ |> should.equal(True)
+}
- pub fn zip_test() {
- list.zip([], [1, 2, 3])
- |> should.equal([])
+pub fn any_test() {
+ list.any([1, 2, 3, 4, 5], fn(x) { x == 2 })
+ |> should.equal(True)
- list.zip([1, 2], [])
- |> should.equal([])
+ list.any([1, 2, 3, 4, 5], fn(x) { x < 0 })
+ |> should.equal(False)
- list.zip([1, 2, 3], [4, 5, 6])
- |> should.equal([#(1, 4), #(2, 5), #(3, 6)])
+ list.any([], fn(_) { False })
+ |> should.equal(False)
+}
- list.zip([5, 6], [1, 2, 3])
- |> should.equal([#(5, 1), #(6, 2)])
+pub fn zip_test() {
+ list.zip([], [1, 2, 3])
+ |> should.equal([])
- list.zip([5, 6, 7], [1, 2])
- |> should.equal([#(5, 1), #(6, 2)])
- }
+ list.zip([1, 2], [])
+ |> should.equal([])
- pub fn strict_zip_test() {
- list.strict_zip([], [1, 2, 3])
- |> should.equal(Error(list.LengthMismatch))
+ list.zip([1, 2, 3], [4, 5, 6])
+ |> should.equal([#(1, 4), #(2, 5), #(3, 6)])
- list.strict_zip([1, 2], [])
- |> should.equal(Error(list.LengthMismatch))
+ list.zip([5, 6], [1, 2, 3])
+ |> should.equal([#(5, 1), #(6, 2)])
- list.strict_zip([1, 2, 3], [4, 5, 6])
- |> should.equal(Ok([#(1, 4), #(2, 5), #(3, 6)]))
+ list.zip([5, 6, 7], [1, 2])
+ |> should.equal([#(5, 1), #(6, 2)])
+}
- list.strict_zip([5, 6], [1, 2, 3])
- |> should.equal(Error(list.LengthMismatch))
+pub fn strict_zip_test() {
+ list.strict_zip([], [1, 2, 3])
+ |> should.equal(Error(list.LengthMismatch))
- list.strict_zip([5, 6, 7], [1, 2])
- |> should.equal(Error(list.LengthMismatch))
- }
+ list.strict_zip([1, 2], [])
+ |> should.equal(Error(list.LengthMismatch))
- pub fn unzip_test() {
- list.unzip([#(1, 2), #(3, 4)])
- |> should.equal(#([1, 3], [2, 4]))
+ list.strict_zip([1, 2, 3], [4, 5, 6])
+ |> should.equal(Ok([#(1, 4), #(2, 5), #(3, 6)]))
- list.unzip([])
- |> should.equal(#([], []))
- }
+ list.strict_zip([5, 6], [1, 2, 3])
+ |> should.equal(Error(list.LengthMismatch))
- pub fn intersperse_test() {
- list.intersperse([1, 2, 3], 4)
- |> should.equal([1, 4, 2, 4, 3])
+ list.strict_zip([5, 6, 7], [1, 2])
+ |> should.equal(Error(list.LengthMismatch))
+}
- list.intersperse([], 2)
- |> should.equal([])
- }
+pub fn unzip_test() {
+ list.unzip([#(1, 2), #(3, 4)])
+ |> should.equal(#([1, 3], [2, 4]))
+
+ list.unzip([])
+ |> should.equal(#([], []))
+}
- pub fn at_test() {
- list.at([1, 2, 3], 2)
- |> should.equal(Ok(3))
+pub fn intersperse_test() {
+ list.intersperse([1, 2, 3], 4)
+ |> should.equal([1, 4, 2, 4, 3])
- list.at([1, 2, 3], 5)
- |> should.equal(Error(Nil))
+ list.intersperse([], 2)
+ |> should.equal([])
+}
- list.at([], 0)
- |> should.equal(Error(Nil))
+pub fn at_test() {
+ list.at([1, 2, 3], 2)
+ |> should.equal(Ok(3))
- list.at([1, 2, 3, 4, 5, 6], -1)
- |> should.equal(Error(Nil))
- }
+ list.at([1, 2, 3], 5)
+ |> should.equal(Error(Nil))
- pub fn unique_test() {
- list.unique([1, 1, 2, 3, 4, 4, 4, 5, 6])
- |> should.equal([1, 2, 3, 4, 5, 6])
+ list.at([], 0)
+ |> should.equal(Error(Nil))
- list.unique([7, 1, 45, 6, 2, 47, 2, 7, 5])
- |> should.equal([7, 1, 45, 6, 2, 47, 5])
+ list.at([1, 2, 3, 4, 5, 6], -1)
+ |> should.equal(Error(Nil))
+}
- list.unique([3, 4, 5])
- |> should.equal([3, 4, 5])
+pub fn unique_test() {
+ list.unique([1, 1, 2, 3, 4, 4, 4, 5, 6])
+ |> should.equal([1, 2, 3, 4, 5, 6])
- list.unique([])
- |> should.equal([])
- }
+ list.unique([7, 1, 45, 6, 2, 47, 2, 7, 5])
+ |> should.equal([7, 1, 45, 6, 2, 47, 5])
- pub fn sort_test() {
- [4, 3, 6, 5, 4]
- |> list.sort(int.compare)
- |> should.equal([3, 4, 4, 5, 6])
+ list.unique([3, 4, 5])
+ |> should.equal([3, 4, 5])
- [4, 3, 6, 5, 4, 1]
- |> list.sort(int.compare)
- |> should.equal([1, 3, 4, 4, 5, 6])
+ list.unique([])
+ |> should.equal([])
+}
- [4.1, 3.1, 6.1, 5.1, 4.1]
- |> list.sort(float.compare)
- |> should.equal([3.1, 4.1, 4.1, 5.1, 6.1])
+pub fn sort_test() {
+ [4, 3, 6, 5, 4]
+ |> list.sort(int.compare)
+ |> should.equal([3, 4, 4, 5, 6])
- []
- |> list.sort(int.compare)
- |> should.equal([])
- }
+ [4, 3, 6, 5, 4, 1]
+ |> list.sort(int.compare)
+ |> should.equal([1, 3, 4, 4, 5, 6])
- pub fn index_map_test() {
- list.index_map([3, 4, 5], fn(i, x) { #(i, x) })
- |> should.equal([#(0, 3), #(1, 4), #(2, 5)])
+ [4.1, 3.1, 6.1, 5.1, 4.1]
+ |> list.sort(float.compare)
+ |> should.equal([3.1, 4.1, 4.1, 5.1, 6.1])
- let f = fn(i, x) { string.append(x, int.to_string(i)) }
- list.index_map(["a", "b", "c"], f)
- |> should.equal(["a0", "b1", "c2"])
- }
+ []
+ |> list.sort(int.compare)
+ |> should.equal([])
+}
- pub fn range_test() {
- list.range(0, 0)
- |> should.equal([])
+pub fn index_map_test() {
+ list.index_map([3, 4, 5], fn(i, x) { #(i, x) })
+ |> should.equal([#(0, 3), #(1, 4), #(2, 5)])
- list.range(1, 1)
- |> should.equal([])
+ let f = fn(i, x) { #(x, i) }
+ list.index_map(["a", "b"], f)
+ |> should.equal([#("a", 0), #("b", 1)])
- list.range(-1, -1)
- |> should.equal([])
+ let f = fn(i, x) { #(x, i) }
+ list.index_map(["a", "b", "c"], f)
+ |> should.equal([#("a", 0), #("b", 1), #("c", 2)])
+}
- list.range(0, 1)
- |> should.equal([0])
+pub fn range_test() {
+ list.range(0, 0)
+ |> should.equal([])
- list.range(0, 5)
- |> should.equal([0, 1, 2, 3, 4])
+ list.range(1, 1)
+ |> should.equal([])
- list.range(1, -5)
- |> should.equal([1, 0, -1, -2, -3, -4])
- }
+ list.range(-1, -1)
+ |> should.equal([])
- pub fn repeat_test() {
- list.repeat(1, -10)
- |> should.equal([])
+ list.range(0, 1)
+ |> should.equal([0])
- list.repeat(1, 0)
- |> should.equal([])
+ list.range(0, 5)
+ |> should.equal([0, 1, 2, 3, 4])
- list.repeat(2, 3)
- |> should.equal([2, 2, 2])
+ list.range(1, -5)
+ |> should.equal([1, 0, -1, -2, -3, -4])
+}
- list.repeat("x", 5)
- |> should.equal(["x", "x", "x", "x", "x"])
- }
+pub fn repeat_test() {
+ list.repeat(1, -10)
+ |> should.equal([])
- pub fn split_test() {
- []
- |> list.split(0)
- |> should.equal(#([], []))
+ list.repeat(1, 0)
+ |> should.equal([])
- [0, 1, 2, 3, 4]
- |> list.split(0)
- |> should.equal(#([], [0, 1, 2, 3, 4]))
+ list.repeat(2, 3)
+ |> should.equal([2, 2, 2])
- [0, 1, 2, 3, 4]
- |> list.split(-2)
- |> should.equal(#([], [0, 1, 2, 3, 4]))
+ list.repeat("x", 5)
+ |> should.equal(["x", "x", "x", "x", "x"])
+}
- [0, 1, 2, 3, 4]
- |> list.split(1)
- |> should.equal(#([0], [1, 2, 3, 4]))
+pub fn split_test() {
+ []
+ |> list.split(0)
+ |> should.equal(#([], []))
- [0, 1, 2, 3, 4]
- |> list.split(3)
- |> should.equal(#([0, 1, 2], [3, 4]))
+ [0, 1, 2, 3, 4]
+ |> list.split(0)
+ |> should.equal(#([], [0, 1, 2, 3, 4]))
- [0, 1, 2, 3, 4]
- |> list.split(9)
- |> should.equal(#([0, 1, 2, 3, 4], []))
- }
+ [0, 1, 2, 3, 4]
+ |> list.split(-2)
+ |> should.equal(#([], [0, 1, 2, 3, 4]))
- pub fn split_while_test() {
- []
- |> list.split_while(fn(x) { x <= 5 })
- |> should.equal(#([], []))
+ [0, 1, 2, 3, 4]
+ |> list.split(1)
+ |> should.equal(#([0], [1, 2, 3, 4]))
- [1, 2, 3, 4, 5]
- |> list.split_while(fn(x) { x <= 5 })
- |> should.equal(#([1, 2, 3, 4, 5], []))
+ [0, 1, 2, 3, 4]
+ |> list.split(3)
+ |> should.equal(#([0, 1, 2], [3, 4]))
- [1, 2, 3, 4, 5]
- |> list.split_while(fn(x) { x == 2 })
- |> should.equal(#([], [1, 2, 3, 4, 5]))
+ [0, 1, 2, 3, 4]
+ |> list.split(9)
+ |> should.equal(#([0, 1, 2, 3, 4], []))
+}
- [1, 2, 3, 4, 5]
- |> list.split_while(fn(x) { x <= 3 })
- |> should.equal(#([1, 2, 3], [4, 5]))
+pub fn split_while_test() {
+ []
+ |> list.split_while(fn(x) { x <= 5 })
+ |> should.equal(#([], []))
- [1, 2, 3, 4, 5]
- |> list.split_while(fn(x) { x <= -3 })
- |> should.equal(#([], [1, 2, 3, 4, 5]))
- }
+ [1, 2, 3, 4, 5]
+ |> list.split_while(fn(x) { x <= 5 })
+ |> should.equal(#([1, 2, 3, 4, 5], []))
- pub fn key_find_test() {
- let proplist = [#(0, "1"), #(1, "2")]
+ [1, 2, 3, 4, 5]
+ |> list.split_while(fn(x) { x == 2 })
+ |> should.equal(#([], [1, 2, 3, 4, 5]))
- proplist
- |> list.key_find(0)
- |> should.equal(Ok("1"))
+ [1, 2, 3, 4, 5]
+ |> list.split_while(fn(x) { x <= 3 })
+ |> should.equal(#([1, 2, 3], [4, 5]))
- proplist
- |> list.key_find(1)
- |> should.equal(Ok("2"))
+ [1, 2, 3, 4, 5]
+ |> list.split_while(fn(x) { x <= -3 })
+ |> should.equal(#([], [1, 2, 3, 4, 5]))
+}
- proplist
- |> list.key_find(2)
- |> should.equal(Error(Nil))
- }
+pub fn key_find_test() {
+ let proplist = [#(0, "1"), #(1, "2")]
- pub fn pop_test() {
- list.pop([1, 2, 3], fn(x) { x > 2 })
- |> should.equal(Ok(#(3, [1, 2])))
+ proplist
+ |> list.key_find(0)
+ |> should.equal(Ok("1"))
- list.pop([1, 2, 3], fn(x) { x > 4 })
- |> should.equal(Error(Nil))
+ proplist
+ |> list.key_find(1)
+ |> should.equal(Ok("2"))
- list.pop([], fn(_x) { True })
- |> should.equal(Error(Nil))
- }
+ proplist
+ |> list.key_find(2)
+ |> should.equal(Error(Nil))
+}
- pub fn pop_map_test() {
- list.pop_map(["foo", "2", "3"], int.parse)
- |> should.equal(Ok(#(2, ["foo", "3"])))
+pub fn pop_test() {
+ list.pop([1, 2, 3], fn(x) { x > 2 })
+ |> should.equal(Ok(#(3, [1, 2])))
- list.pop_map(["foo", "bar"], int.parse)
- |> should.equal(Error(Nil))
+ list.pop([1, 2, 3], fn(x) { x > 4 })
+ |> should.equal(Error(Nil))
- list.pop_map([], int.parse)
- |> should.equal(Error(Nil))
+ list.pop([], fn(_x) { True })
+ |> should.equal(Error(Nil))
+}
+
+pub fn pop_map_test() {
+ let get = fn(x) {
+ case x > 0 {
+ True -> Ok(x * 2)
+ False -> Error(Nil)
+ }
}
+ list.pop_map([0, 2, 3], get)
+ |> should.equal(Ok(#(4, [0, 3])))
- pub fn key_pop_test() {
- list.key_pop([#("a", 0), #("b", 1)], "a")
- |> should.equal(Ok(#(0, [#("b", 1)])))
+ list.pop_map([0, -1], get)
+ |> should.equal(Error(Nil))
- list.key_pop([#("a", 0), #("b", 1)], "b")
- |> should.equal(Ok(#(1, [#("a", 0)])))
+ list.pop_map([], get)
+ |> should.equal(Error(Nil))
+}
- list.key_pop([#("a", 0), #("b", 1)], "c")
- |> should.equal(Error(Nil))
- }
+pub fn key_pop_test() {
+ list.key_pop([#("a", 0), #("b", 1)], "a")
+ |> should.equal(Ok(#(0, [#("b", 1)])))
- pub fn key_set_test() {
- [#(5, 0), #(4, 1)]
- |> list.key_set(4, 100)
- |> should.equal([#(5, 0), #(4, 100)])
+ list.key_pop([#("a", 0), #("b", 1)], "b")
+ |> should.equal(Ok(#(1, [#("a", 0)])))
- [#(5, 0), #(4, 1)]
- |> list.key_set(1, 100)
- |> should.equal([#(5, 0), #(4, 1), #(1, 100)])
- }
+ list.key_pop([#("a", 0), #("b", 1)], "c")
+ |> should.equal(Error(Nil))
+}
- pub fn partition_test() {
- [1, 2, 3, 4, 5, 6, 7]
- |> list.partition(int.is_odd)
- |> should.equal(#([1, 3, 5, 7], [2, 4, 6]))
- }
+pub fn key_set_test() {
+ [#(5, 0), #(4, 1)]
+ |> list.key_set(4, 100)
+ |> should.equal([#(5, 0), #(4, 100)])
- pub fn permutations_test() {
- [1, 2]
- |> list.permutations
- |> should.equal([[1, 2], [2, 1]])
-
- let expected = [
- [1, 2, 3],
- [1, 3, 2],
- [2, 1, 3],
- [2, 3, 1],
- [3, 1, 2],
- [3, 2, 1],
- ]
-
- [1, 2, 3]
- |> list.permutations
- |> should.equal(expected)
-
- ["a", "b"]
- |> list.permutations
- |> should.equal([["a", "b"], ["b", "a"]])
- }
+ [#(5, 0), #(4, 1)]
+ |> list.key_set(1, 100)
+ |> should.equal([#(5, 0), #(4, 1), #(1, 100)])
+}
- pub fn window_test() {
- [1, 2, 3]
- |> list.window(by: 2)
- |> should.equal([[1, 2], [2, 3]])
+pub fn partition_test() {
+ [1, 2, 3, 4, 5, 6, 7]
+ |> list.partition(int.is_odd)
+ |> should.equal(#([1, 3, 5, 7], [2, 4, 6]))
+}
- [1, 2, 3]
- |> list.window(3)
- |> should.equal([[1, 2, 3]])
+pub fn permutations_test() {
+ [1, 2]
+ |> list.permutations
+ |> should.equal([[1, 2], [2, 1]])
+
+ let expected = [
+ [1, 2, 3],
+ [1, 3, 2],
+ [2, 1, 3],
+ [2, 3, 1],
+ [3, 1, 2],
+ [3, 2, 1],
+ ]
+
+ [1, 2, 3]
+ |> list.permutations
+ |> should.equal(expected)
+
+ ["a", "b"]
+ |> list.permutations
+ |> should.equal([["a", "b"], ["b", "a"]])
+}
- [1, 2, 3]
- |> list.window(4)
- |> should.equal([])
+pub fn window_test() {
+ [1, 2, 3]
+ |> list.window(by: 2)
+ |> should.equal([[1, 2], [2, 3]])
- [1, 2, 3, 4, 5]
- |> list.window(3)
- |> should.equal([[1, 2, 3], [2, 3, 4], [3, 4, 5]])
- }
+ [1, 2, 3]
+ |> list.window(3)
+ |> should.equal([[1, 2, 3]])
- pub fn window_by_2_test() {
- [1, 2, 3, 4]
- |> list.window_by_2
- |> should.equal([#(1, 2), #(2, 3), #(3, 4)])
+ [1, 2, 3]
+ |> list.window(4)
+ |> should.equal([])
- [1]
- |> list.window_by_2
- |> should.equal([])
- }
+ [1, 2, 3, 4, 5]
+ |> list.window(3)
+ |> should.equal([[1, 2, 3], [2, 3, 4], [3, 4, 5]])
+}
- pub fn drop_while_test() {
- [1, 2, 3, 4]
- |> list.drop_while(fn(x) { x < 3 })
- |> should.equal([3, 4])
- }
+pub fn window_by_2_test() {
+ [1, 2, 3, 4]
+ |> list.window_by_2
+ |> should.equal([#(1, 2), #(2, 3), #(3, 4)])
- pub fn take_while_test() {
- [1, 2, 3, 2, 4]
- |> list.take_while(fn(x) { x < 3 })
- |> should.equal([1, 2])
- }
+ [1]
+ |> list.window_by_2
+ |> should.equal([])
+}
- pub fn chunk_test() {
- [1, 2, 2, 3, 4, 4, 6, 7, 7]
- |> list.chunk(by: fn(n) { n % 2 })
- |> should.equal([[1], [2, 2], [3], [4, 4, 6], [7, 7]])
- }
+pub fn drop_while_test() {
+ [1, 2, 3, 4]
+ |> list.drop_while(fn(x) { x < 3 })
+ |> should.equal([3, 4])
+}
- pub fn sized_chunk_test() {
- [1, 2, 3, 4, 5, 6]
- |> list.sized_chunk(into: 2)
- |> should.equal([[1, 2], [3, 4], [5, 6]])
+pub fn take_while_test() {
+ [1, 2, 3, 2, 4]
+ |> list.take_while(fn(x) { x < 3 })
+ |> should.equal([1, 2])
+}
- [1, 2, 3, 4, 5, 6, 7, 8]
- |> list.sized_chunk(into: 3)
- |> should.equal([[1, 2, 3], [4, 5, 6], [7, 8]])
- }
+pub fn chunk_test() {
+ [1, 2, 2, 3, 4, 4, 6, 7, 7]
+ |> list.chunk(by: fn(n) { n % 2 })
+ |> should.equal([[1], [2, 2], [3], [4, 4, 6], [7, 7]])
+}
- pub fn reduce_test() {
- []
- |> list.reduce(with: fn(x, y) { x + y })
- |> should.equal(Error(Nil))
+pub fn sized_chunk_test() {
+ [1, 2, 3, 4, 5, 6]
+ |> list.sized_chunk(into: 2)
+ |> should.equal([[1, 2], [3, 4], [5, 6]])
- [1, 2, 3, 4, 5]
- |> list.reduce(with: fn(x, y) { x + y })
- |> should.equal(Ok(15))
- }
+ [1, 2, 3, 4, 5, 6, 7, 8]
+ |> list.sized_chunk(into: 3)
+ |> should.equal([[1, 2, 3], [4, 5, 6], [7, 8]])
+}
- pub fn scan_test() {
- []
- |> list.scan(from: 0, with: fn(i, acc) { i + acc })
- |> should.equal([])
-
- [1, 2, 3, 4]
- |> list.scan(from: 0, with: fn(i, acc) { 2 * i + acc })
- |> should.equal([2, 6, 12, 20])
-
- [1, 2, 3, 4]
- |> list.scan(
- from: "",
- with: fn(i, acc) {
- case int.is_even(i) {
- True -> "Even"
- False -> "Odd"
- }
- |> string.append(acc, _)
- },
- )
- |> should.equal(["Odd", "OddEven", "OddEvenOdd", "OddEvenOddEven"])
- }
+pub fn reduce_test() {
+ []
+ |> list.reduce(with: fn(x, y) { x + y })
+ |> should.equal(Error(Nil))
- pub fn last_test() {
- list.last([])
- |> should.equal(Error(Nil))
+ [1, 2, 3, 4, 5]
+ |> list.reduce(with: fn(x, y) { x + y })
+ |> should.equal(Ok(15))
+}
- list.last([1, 2, 3, 4, 5])
- |> should.equal(Ok(5))
- }
+pub fn scan_test() {
+ []
+ |> list.scan(from: 0, with: fn(i, acc) { i + acc })
+ |> should.equal([])
+
+ [1, 2, 3, 4]
+ |> list.scan(from: 0, with: fn(i, acc) { 2 * i + acc })
+ |> should.equal([2, 6, 12, 20])
+
+ [1, 2, 3, 4]
+ |> list.scan(
+ from: [],
+ with: fn(i, acc) {
+ case int.is_even(i) {
+ True -> ["Even", ..acc]
+ False -> ["Odd", ..acc]
+ }
+ },
+ )
+ |> should.equal([
+ ["Odd"],
+ ["Even", "Odd"],
+ ["Odd", "Even", "Odd"],
+ ["Even", "Odd", "Even", "Odd"],
+ ])
+}
- pub fn combinations_test() {
- list.combinations([1, 2, 3], by: 0)
- |> should.equal([[]])
+pub fn last_test() {
+ list.last([])
+ |> should.equal(Error(Nil))
- list.combinations([1, 2, 3], by: 1)
- |> should.equal([[1], [2], [3]])
+ list.last([1, 2, 3, 4, 5])
+ |> should.equal(Ok(5))
+}
- list.combinations([1, 2, 3], by: 2)
- |> should.equal([[1, 2], [1, 3], [2, 3]])
+pub fn combinations_test() {
+ list.combinations([1, 2, 3], by: 0)
+ |> should.equal([[]])
- list.combinations([1, 2, 3], by: 3)
- |> should.equal([[1, 2, 3]])
+ list.combinations([1, 2, 3], by: 1)
+ |> should.equal([[1], [2], [3]])
- list.combinations([1, 2, 3], by: 4)
- |> should.equal([])
+ list.combinations([1, 2, 3], by: 2)
+ |> should.equal([[1, 2], [1, 3], [2, 3]])
- list.combinations([1, 2, 3, 4], 3)
- |> should.equal([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]])
- }
+ list.combinations([1, 2, 3], by: 3)
+ |> should.equal([[1, 2, 3]])
- pub fn combination_pairs_test() {
- list.combination_pairs([1])
- |> should.equal([])
+ list.combinations([1, 2, 3], by: 4)
+ |> should.equal([])
- list.combination_pairs([1, 2])
- |> should.equal([#(1, 2)])
+ list.combinations([1, 2, 3, 4], 3)
+ |> should.equal([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]])
+}
- list.combination_pairs([1, 2, 3])
- |> should.equal([#(1, 2), #(1, 3), #(2, 3)])
+pub fn combination_pairs_test() {
+ list.combination_pairs([1])
+ |> should.equal([])
- list.combination_pairs([1, 2, 3, 4])
- |> should.equal([#(1, 2), #(1, 3), #(1, 4), #(2, 3), #(2, 4), #(3, 4)])
- }
+ list.combination_pairs([1, 2])
+ |> should.equal([#(1, 2)])
- pub fn interleave_test() {
- list.interleave([[1, 2], [101, 102]])
- |> should.equal([1, 101, 2, 102])
+ list.combination_pairs([1, 2, 3])
+ |> should.equal([#(1, 2), #(1, 3), #(2, 3)])
- list.interleave([[1, 2], [101, 102], [201, 202]])
- |> should.equal([1, 101, 201, 2, 102, 202])
+ list.combination_pairs([1, 2, 3, 4])
+ |> should.equal([#(1, 2), #(1, 3), #(1, 4), #(2, 3), #(2, 4), #(3, 4)])
+}
- // Left over elements are added at the end
- list.interleave([[1, 2, 3], [101, 102]])
- |> should.equal([1, 101, 2, 102, 3])
+pub fn interleave_test() {
+ list.interleave([[1, 2], [101, 102]])
+ |> should.equal([1, 101, 2, 102])
- list.interleave([[1, 2], [101, 102, 103]])
- |> should.equal([1, 101, 2, 102, 103])
- }
+ list.interleave([[1, 2], [101, 102], [201, 202]])
+ |> should.equal([1, 101, 201, 2, 102, 202])
- pub fn transpose_test() {
- list.transpose([[1, 2, 3], [101, 102, 103]])
- |> should.equal([[1, 101], [2, 102], [3, 103]])
+ // Left over elements are added at the end
+ list.interleave([[1, 2, 3], [101, 102]])
+ |> should.equal([1, 101, 2, 102, 3])
- list.transpose([[1, 2, 3], [101, 102, 103], [201, 202, 203]])
- |> should.equal([[1, 101, 201], [2, 102, 202], [3, 103, 203]])
+ list.interleave([[1, 2], [101, 102, 103]])
+ |> should.equal([1, 101, 2, 102, 103])
+}
- // Left over elements are still returned
- list.transpose([[1, 2], [101, 102, 103]])
- |> should.equal([[1, 101], [2, 102], [103]])
+pub fn transpose_test() {
+ list.transpose([[1, 2, 3], [101, 102, 103]])
+ |> should.equal([[1, 101], [2, 102], [3, 103]])
- list.transpose([[1, 2, 3], [101, 102], [201, 202, 203]])
- |> should.equal([[1, 101, 201], [2, 102, 202], [3, 203]])
- }
+ list.transpose([[1, 2, 3], [101, 102, 103], [201, 202, 203]])
+ |> should.equal([[1, 101, 201], [2, 102, 202], [3, 103, 203]])
+
+ // Left over elements are still returned
+ list.transpose([[1, 2], [101, 102, 103]])
+ |> should.equal([[1, 101], [2, 102], [103]])
+
+ list.transpose([[1, 2, 3], [101, 102], [201, 202, 203]])
+ |> should.equal([[1, 101, 201], [2, 102, 202], [3, 203]])
}