List
is_empty : List * -> Bool
Check if the list is empty.
List.is_empty([1, 2, 3]) List.is_empty([])
get : List a, U64 -> Result a [OutOfBounds]
Returns an element from a list at the given index.
Returns Err OutOfBounds
if the given index exceeds the List's length
expect List.get([100, 200, 300], 1) == Ok(200) expect List.get([100, 200, 300], 5) == Err(OutOfBounds)
replace :
List a,
U64,
a
->
{
list : List a,
value : a
}
set :
List a,
U64,
a
-> List a
Replaces the element at the given index with a replacement.
List.set(["a", "b", "c"], 1, "B")
If the given index is outside the bounds of the list, returns the original list unmodified.
To drop the element at a given index, instead of replacing it, see List.drop_at
.
update :
List a,
U64,
(a -> a)
-> List a
Updates the element at the given index with the given function.
List.update([1, 2, 3], 1, (\x -> x + 1))
If the given index is outside the bounds of the list, returns the original list unmodified.
To replace the element at a given index, instead of updating based on the current value,
see List.set
and List.replace
append : List a, a -> List a
Add a single element to the end of a list.
List.append([1, 2, 3], 4) [0, 1, 2] |> List.append(3)
append_if_ok : List a, Result a * -> List a
If the given Result
is Ok
, add it to the end of a list.
Otherwise, return the list unmodified.
List.append_if_ok([1, 2, 3], Ok(4)) [0, 1, 2] |> List.append_if_ok(Err(3))
prepend : List a, a -> List a
Add a single element to the beginning of a list.
List.prepend([1, 2, 3], 0) [2, 3, 4] |> List.prepend(1)
prepend_if_ok : List a, Result a * -> List a
If the given Result
is Ok
, add it to the beginning of a list.
Otherwise, return the list unmodified.
List.prepend([1, 2, 3], Ok(0)) [2, 3, 4] |> List.prepend(Err(1))
len : List * -> U64
Returns the length of the list - the number of elements it contains.
One List
can store up to Num.max_i64
elements on 64-bit targets and Num.max_i32
on 32-bit targets like wasm.
This means the #U64 this function returns can always be safely converted to #I64 or #I32, depending on the target.
with_capacity : U64 -> List *
Create a list with space for at least capacity elements
reserve : List a, U64 -> List a
Enlarge the list for at least capacity additional elements
release_excess_capacity : List a -> List a
Shrink the memory footprint of a list such that it's capacity and length are equal. Note: This will also convert seamless slices to regular lists.
concat : List a, List a -> List a
Put two lists together.
List.concat([1, 2, 3], [4, 5]) [0, 1, 2] |> List.concat([3, 4])
last : List a -> Result a [ListWasEmpty]
Returns the last element in the list, or ListWasEmpty
if it was empty.
expect List.last([1, 2, 3]) == Ok(3) expect List.last([]) == Err(ListWasEmpty)
single : a -> List a
A list with a single element in it.
This is useful in pipelines, like so:
websites = Str.concat(domain, ".com") |> List.single
repeat : a, U64 -> List a
Returns a list with the given length, where every element is the given value.
reverse : List a -> List a
Returns the list with its elements reversed.
expect List.reverse([1, 2, 3]) == [3, 2, 1]
join : List (List a) -> List a
Join the given lists together into one list.
expect List.join([[1], [2, 3], [], [4, 5]]) == [1, 2, 3, 4, 5] expect List.join([[], []]) == [] expect List.join([]) == []
contains : List a, a -> Bool
where a implements Eq
walk :
List elem,
state,
(state, elem -> state)
-> state
Build a value using each element in the list.
Starting with a given state
value, this walks through each element in the
list from first to last, running a given step
function on that element
which updates the state
. It returns the final state
at the end.
You can use it in a pipeline:
[2, 4, 8] |> List.walk(0, Num.add)
This returns 14 because:
state
starts at 0- Each
step
runsNum.add(state, elem)
, and the return value becomes the newstate
.
Here is a table of how state
changes as List.walk
walks over the elements
[2, 4, 8]
using Num.add
as its step
function to determine the next state
.
state | elem | Num.add state elem |
---|---|---|
0 | ||
0 | 2 | 2 |
2 | 4 | 6 |
6 | 8 | 14 |
The following returns -6:
[1, 2, 3] |> List.walk(0, Num.sub)
Note that in other languages, walk
is sometimes called reduce
,
fold
, fold_left
, or foldl
.
walk_with_index :
List elem,
state,
(state,
elem,
U64
-> state)
-> state
Like walk
, but at each step the function also receives the index of the current element.
walk_with_index_until :
List elem,
state,
(state,
elem,
U64
->
[
Continue state,
Break state
])
-> state
Like walk_until
, but at each step the function also receives the index of the current element.
walk_backwards :
List elem,
state,
(state, elem -> state)
-> state
Note that in other languages, walk_backwards
is sometimes called reduce_right
,
fold
, fold_right
, or foldr
.
walk_until :
List elem,
state,
(state,
elem
->
[
Continue state,
Break state
])
-> state
Same as List.walk
, except you can stop walking early.
Performance Details
Compared to List.walk
, this can potentially visit fewer elements (which can
improve performance) at the cost of making each step take longer.
However, the added cost to each step is extremely small, and can easily
be outweighed if it results in skipping even a small number of elements.
As such, it is typically better for performance to use this over List.walk
if returning Break
earlier than the last element is expected to be common.
walk_backwards_until :
List elem,
state,
(state,
elem
->
[
Continue state,
Break state
])
-> state
Same as List.walk_until
, but does it from the end of the list instead.
walk_from :
List elem,
U64,
state,
(state, elem -> state)
-> state
Walks to the end of the list from a specified starting index
walk_from_until :
List elem,
U64,
state,
(state,
elem
->
[
Continue state,
Break state
])
-> state
A combination of List.walk_from
and List.walk_until
sum : List (Num a) -> Num a
product : List (Num a) -> Num a
any : List a, (a -> Bool) -> Bool
Run the given predicate on each element of the list, returning Bool.true
if
any of the elements satisfy it.
all : List a, (a -> Bool) -> Bool
Run the given predicate on each element of the list, returning Bool.true
if
all of the elements satisfy it.
keep_if : List a, (a -> Bool) -> List a
Run the given function on each element of a list, and return all the
elements for which the function returned Bool.true
.
List.keep_if([1, 2, 3, 4], (\num -> num > 2))
Performance Details
List.keep_if
always returns a list that takes up exactly the same amount
of memory as the original, even if its length decreases. This is because it
can't know in advance exactly how much space it will need, and if it guesses a
length that's too low, it would have to re-allocate.
(If you want to do an operation like this which reduces the memory footprint
of the resulting list, you can do two passes over the list with List.walk
- one
to calculate the precise new size, and another to populate the new list.)
If given a unique list, List.keep_if
will mutate it in place to assemble the appropriate list.
If that happens, this function will not allocate any new memory on the heap.
If all elements in the list end up being kept, Roc will return the original
list unaltered.
drop_if : List a, (a -> Bool) -> List a
Run the given function on each element of a list, and return all the
elements for which the function returned Bool.false
.
List.drop_if([1, 2, 3, 4], (\num -> num > 2))
Performance Details
List.drop_if
has the same performance characteristics as List.keep_if
.
See its documentation for details on those characteristics!
count_if : List a, (a -> Bool) -> U64
Run the given function on each element of a list, and return the
number of elements for which the function returned Bool.true
.
expect List.count_if([1, -2, -3], Num.is_negative) == 2 expect List.count_if([1, 2, 3], (\num -> num > 1)) == 2
keep_oks : List before, (before -> Result after *) -> List after
This works like List.map
, except only the transformed values that are
wrapped in Ok
are kept. Any that are wrapped in Err
are dropped.
expect List.keep_oks(["1", "Two", "23", "Bird"], Str.to_i32) == [1, 23] expect List.keep_oks([["a", "b"], [], ["c", "d", "e"], [] ], List.first) == ["a", "c"] fn = \str -> if Str.is_empty(str) then Err(StrWasEmpty) else Ok(str) expect List.keep_oks(["", "a", "bc", "", "d", "ef", ""], fn) == ["a", "bc", "d", "ef"]
keep_errs : List before, (before -> Result * after) -> List after
This works like List.map
, except only the transformed values that are
wrapped in Err
are kept. Any that are wrapped in Ok
are dropped.
List.keep_errs([["a", "b"], [], [], ["c", "d", "e"]], List.last) fn = \str -> if Str.is_empty(str) then Err(StrWasEmpty) else Okd(Str.len(str)) List.keep_errs(["", "a", "bc", "", "d", "ef", ""], fn)
map : List a, (a -> b) -> List b
Convert each element in the list to something new, by calling a conversion function on each of them. Then return a new list of the converted values.
expect List.map([1, 2, 3], (\num -> num + 1)) == [2, 3, 4] expect List.map(["", "a", "bc"], Str.is_empty) == [Bool.true, Bool.false, Bool.false]
map2 :
List a,
List b,
(a, b -> c)
-> List c
Run a transformation function on the first element of each list, and use that as the first element in the returned list. Repeat until a list runs out of elements.
Some languages have a function named zip
, which does something similar to
calling List.map2
passing two lists and Pair
:
zipped = List.map2(["a", "b", "c"], [1, 2, 3], Pair)
map3 :
List a,
List b,
List c,
(a,
b,
c
-> d)
-> List d
Run a transformation function on the first element of each list, and use that as the first element in the returned list. Repeat until a list runs out of elements.
map4 :
List a,
List b,
List c,
List d,
(a,
b,
c,
d
-> e)
-> List e
Run a transformation function on the first element of each list, and use that as the first element in the returned list. Repeat until a list runs out of elements.
map_with_index : List a, (a, U64 -> b) -> List b
This works like List.map
, except it also passes the index
of the element to the conversion function.
expect List.map_with_index([10, 20, 30], (\num, index -> num + index)) == [10, 21, 32]
range
Returns a list of all the integers between start
and end
.
To include the start
and end
integers themselves, use At
like so:
List.range({ start: At 2, end: At 5 }) == [2, 3, 4, 5]
To exclude them, use After
and Before
, like so:
List.range({ start: After 2, end: Before 5 }) == [3, 4]
You can have the list end at a certain length rather than a certain integer:
List.range({ start: At 6, end: Length 4 }) == [6, 7, 8, 9]
If step
is specified, each integer increases by that much. (step: 1
is the default.)
List.range({ start: After 0, end: Before 9, step: 3 }) == [3, 6]
List.range will also generate a reversed list if step is negative or end comes before start:
List.range({ start: At 5, end: At 2 }) == [5, 4, 3, 2]
All of these options are compatible with the others. For example, you can use At
or After
with start
regardless of what end
and step
are set to.
sort_with :
List a,
(a,
a
->
[
LT,
EQ,
GT
])
-> List a
Sort with a custom comparison function
sort_asc : List (Num a) -> List (Num a)
Sorts a list of numbers in ascending order (lowest to highest).
To sort in descending order (highest to lowest), use List.sort_desc
instead.
sort_desc : List (Num a) -> List (Num a)
Sorts a list of numbers in descending order (highest to lowest).
To sort in ascending order (lowest to highest), use List.sort_asc
instead.
swap :
List a,
U64,
U64
-> List a
first : List a -> Result a [ListWasEmpty]
Returns the first element in the list, or ListWasEmpty
if it was empty.
take_first : List elem, U64 -> List elem
Returns the given number of elements from the beginning of the list.
List.take_first([1, 2, 3, 4, 5, 6, 7, 8], 4) == [1, 2, 3, 4]
If there are fewer elements in the list than the requested number, returns the entire list.
List.take_first([1, 2], 5) == [1, 2]
To remove elements from the beginning of the list, use List.take_last
.
To remove elements from both the beginning and end of the list,
use List.sublist
.
To split the list into two lists, use List.split_at
.
take_last : List elem, U64 -> List elem
Returns the given number of elements from the end of the list.
List.take_last([1, 2, 3, 4, 5, 6, 7, 8], 4) == [5, 6, 7, 8]
If there are fewer elements in the list than the requested number, returns the entire list.
List.take_last([1, 2], 5) == [1, 2]
To remove elements from the end of the list, use List.take_first
.
To remove elements from both the beginning and end of the list,
use List.sublist
.
To split the list into two lists, use List.split_at
.
drop_first : List elem, U64 -> List elem
Drops n elements from the beginning of the list.
drop_last : List elem, U64 -> List elem
Drops n elements from the end of the list.
drop_at : List elem, U64 -> List elem
Drops the element at the given index from the list.
This has no effect if the given index is outside the bounds of the list.
To replace the element at a given index, instead of dropping it, see List.set
.
min : List (Num a) -> Result (Num a) [ListWasEmpty]
max : List (Num a) -> Result (Num a) [ListWasEmpty]
join_map : List a, (a -> List b) -> List b
Like List.map
, except the transformation function wraps the return value
in a list. At the end, all the lists get joined together into one list.
You may know a similar function named concat_map
in other languages.
find_first : List elem, (elem -> Bool) -> Result elem [NotFound]
Returns the first element of the list satisfying a predicate function.
If no satisfying element is found, an Err NotFound
is returned.
find_last : List elem, (elem -> Bool) -> Result elem [NotFound]
Returns the last element of the list satisfying a predicate function.
If no satisfying element is found, an Err NotFound
is returned.
find_first_index : List elem, (elem -> Bool) -> Result U64 [NotFound]
Returns the index at which the first element in the list
satisfying a predicate function can be found.
If no satisfying element is found, an Err NotFound
is returned.
find_last_index : List elem, (elem -> Bool) -> Result U64 [NotFound]
Returns the last index at which the first element in the list
satisfying a predicate function can be found.
If no satisfying element is found, an Err NotFound
is returned.
sublist :
List elem,
{
start : U64,
len : U64
}
-> List elem
Returns a subsection of the given list, beginning at the start
index and
including a total of len
elements.
If start
is outside the bounds of the given list, returns the empty list.
List.sublist([1, 2, 3], { start: 4, len: 0 })
If more elements are requested than exist in the list, returns as many as it can.
List.sublist([1, 2, 3, 4, 5], { start: 2, len: 10 })
If you want a sublist which goes all the way to the end of the list, no matter how long the list is,
List.take_last
can do that more efficiently.
Some languages have a function called slice
which works similarly to this.
intersperse : List elem, elem -> List elem
Intersperses sep
between the elements of list
List.intersperse([1, 2, 3], 9) == [1, 9, 2, 9, 3]
starts_with : List elem, List elem -> Bool
where elem implements Eq
Returns Bool.true
if the first list starts with the second list.
If the second list is empty, this always returns Bool.true
; every list
is considered to "start with" an empty list.
If the first list is empty, this only returns Bool.true
if the second list is empty.
ends_with : List elem, List elem -> Bool
where elem implements Eq
Returns Bool.true
if the first list ends with the second list.
If the second list is empty, this always returns Bool.true
; every list
is considered to "end with" an empty list.
If the first list is empty, this only returns Bool.true
if the second list is empty.
split_at :
List elem,
U64
->
{
before : List elem,
others : List elem
}
Splits the list into two lists, around the given index.
The returned lists are labeled before
and others
. The before
list will
contain all the elements whose index in the original list was less than
than the given index, # and the others
list will be all the others. (This
means if you give an index of 0, the before
list will be empty and the
others
list will have the same elements as the original list.)
split_on : List a, a -> List (List a)
where a implements Eq
Splits the input list on the delimiter element.
List.split_on([1, 2, 3], 2) == [[1], [3]]
split_on_list : List a, List a -> List (List a)
where a implements Eq
Splits the input list on the delimiter list.
List.split_on_list([1, 2, 3], [1, 2]) == [[], [3]]
split_first :
List elem,
elem
-> Result
{
before : List elem,
after : List elem
} [NotFound]
where elem implements Eq
Returns the elements before the first occurrence of a delimiter, as well as the
remaining elements after that occurrence. If the delimiter is not found, returns Err
.
List.split_first([Foo, Z, Bar, Z, Baz], Z) == Ok({ before: [Foo], after: [Bar, Z, Baz] })
split_last :
List elem,
elem
-> Result
{
before : List elem,
after : List elem
} [NotFound]
where elem implements Eq
Returns the elements before the last occurrence of a delimiter, as well as the
remaining elements after that occurrence. If the delimiter is not found, returns Err
.
List.split_last([Foo, Z, Bar, Z, Baz], Z) == Ok({ before: [Foo, Z, Bar], after: [Baz] })
chunks_of : List a, U64 -> List (List a)
Splits the list into many chunks, each of which is length of the given chunk size. The last chunk will be shorter if the list does not evenly divide by the chunk size. If the provided list is empty or if the chunk size is 0 then the result is an empty list.
map_try : List elem, (elem -> Result ok err) -> Result (List ok) err
Like List.map
, except the transformation function returns a Result
.
If that function ever returns Err
, map_try
immediately returns that Err
.
If it returns Ok
for every element, map_try
returns Ok
with the transformed list.
walk_try :
List elem,
state,
(state, elem -> Result state err)
-> Result state err
Same as List.walk
, except you can stop walking early by returning Err
.
Performance Details
Compared to List.walk
, this can potentially visit fewer elements (which can
improve performance) at the cost of making each step take longer.
However, the added cost to each step is extremely small, and can easily
be outweighed if it results in skipping even a small number of elements.
As such, it is typically better for performance to use this over List.walk
if returning Break
earlier than the last element is expected to be common.
concat_utf8 : List U8, Str -> List U8
Concatenates the bytes of a string encoded as utf8 to a list of bytes.
expect List.concat_utf8([1, 2, 3, 4], "🐦") == [1, 2, 3, 4, 240, 159, 144, 166]
for_each! : List a, (a => {}) => {}
Run an effectful function for each element on the list.
List.for_each!(["Alice", "Bob", "Charlie"], \name -> create_account!(name) log!("Account created") )
If the function might fail or you need to return early, use for_each_try!
.
for_each_try! : List a, (a => Result {} err) => Result {} err
Run an effectful function that might fail for each element on the list.
If the function returns Err
, the iteration stops and the error is returned.
List.for_each_try!(files_to_delete, \path -> File.delete!(path)? Stdout.line!("${path} deleted") )
walk! :
List elem,
state,
(state, elem => state)
=> state
Build a value from the contents of a list, using an effectful function.
now_multiples = List.walk!([1, 2, 3], [], \nums, i -> now = Utc.now!({}) |> Utc.to_millis_since_epoch List.append(nums, now * i) )
This is the same as walk
, except that the step function can have effects.
walk_try! :
List elem,
state,
(state, elem => Result state err)
=> Result state err
Build a value from the contents of a list, using an effectful function that might fail.
If the function returns Err
, the iteration stops and the error is returned.
names = List.walk_try!( ["First", "Middle", "Last"], [], \accumulator, which -> Stdout.write!("${which} name: ")? name = Stdin.line!({})? Ok(List.append(accumulator, name)), )?
This is the same as walk_try
, except that the step function can have effects.