split_at fn
fn split_at[A](xs: List[A], i: Int) -> (List[A], List[A])(xs.take(i), xs.drop(i)) with i clamped to 0..=xs.len: a negative i gives ([], xs) and an i past the end gives (xs, []).
Package
List utilities beyond the builtins: windows, splits, grouping, zips, set operations, combinatorics, searching and safe index edits
0.1.0 MIT
sspur add lists@^0.1.0or in sspur.toml:
lists = { version = "^0.1.0" }List utilities for SSPUR that the builtin List methods don't cover: splitting and sliding windows, run grouping, counting into maps, zips of every shape, order-preserving set and multiset operations, combinatorics with a safety limit, binary search by key, and index edits that never trap. It is the SSPUR counterpart of Guava's Lists/Iterables, Python's itertools and more-itertools, and lodash's array functions.
Every function is pure and generic. A function that takes a lambda performs whatever the lambda performs (f: A -> B ! e), so you can pass logging or failing functions. Out-of-range indexes and counts never trap: each function says what it does with them (usually clamping, or returning the list unchanged). The combinatoric functions have a pre clause that limits their output to combinatorics_limit() (1,000,000) lists.
use lists.{chunk_by, top_k, sliding, combinations, index_by, split_on}
type Order = {id: Int, customer: Str, cents: Int} derive Eq, Show
fn main() -> Unit ! log
= do
orders = [Order{id: 1, customer: "ana", cents: 1200}, Order{id: 2, customer: "ana", cents: 300}, Order{id: 3, customer: "bo", cents: 4500}, Order{id: 4, customer: "cy", cents: 800}]
log(top_k(orders, 2, _.cents).map(_.id).str)
log(chunk_by(orders, _.customer).map(g => "{g.0}: {g.1.len}").join(", "))
log(index_by(orders, _.id).get(3).map(_.customer).str)
log(sliding([1, 2, 3, 4, 5], 2, 2, true).str)
log(combinations(["a", "b", "c"], 2).str)
log(split_on(["x", "", "y", "z", ""], "").str)
log(lists.cartesian([1, 2], ["s", "m"]).str)
Output (the same natively and with --interp):
[3, 1]
ana: 2, bo: 1, cy: 1
some("bo")
[[1, 2], [3, 4], [5]]
[["a", "b"], ["a", "c"], ["b", "c"]]
[["x"], ["y", "z"], []]
[(1, "s"), (1, "m"), (2, "s"), (2, "m")]
Each function has a doc comment with its exact edge-case behaviour; sspur api lists them.
| Function | What it does |
|---|---|
split_at(xs, i) | (take(i), drop(i)), i clamped |
split_while(xs, p), split_until(xs, p) | Haskell's span and break |
split_when(xs, p) | split between neighbours where p(a, b) holds |
split_on(xs, sep), split_where(xs, p) | split at separators, dropping them, like Str.split |
split_parts(xs, n) | exactly n parts of nearly equal length |
sliding(xs, size, step, partial) | windows with a step, optionally keeping shortened tail windows |
chunks_padded(xs, n, fill) | chunks with the last chunk padded |
chunk_by(xs, key) | runs of equal keys, with the key |
rle(xs), rle_decode(runs) | run-length encoding |
tails(xs), inits(xs) | all suffixes, all prefixes |
take_last, drop_last, take_last_while, drop_last_while | the end of a list |
pad_to(xs, n, fill), pad_front_to(xs, n, fill) | pad to a minimum length |
| Function | What it does |
|---|---|
dedup(xs), dedup_by(xs, key) | collapse runs of equal neighbours |
distinct_by(xs, key) | first element per key |
duplicates(xs) | values that occur more than once |
all_equal(xs), all_distinct(xs) | uniformity checks |
single(xs) | the only element, if there is exactly one |
| Function | What it does |
|---|---|
frequencies(xs) | Map of value to count |
count_by(xs, key), count_where(xs, p) | counts by key, or matching a predicate |
index_by(xs, key) | Map of key to element, the last wins |
associate(xs, f) | Map from the pairs f gives, the last wins |
group_into(xs, key) | Map of key to its elements in input order |
| Function | What it does |
|---|---|
sum_by(xs, f), sum_by_f64(xs, f) | sums, the float one compensated (Kahan-Babuska) |
min_by, max_by, min_max, min_max_by | extremes, the first among equals |
top_k(xs, k, key), bottom_k(xs, k, key) | the k best by key, stable |
largest(xs, k), smallest(xs, k) | the k largest or smallest elements |
| Function | What it does |
|---|---|
interleave(xs, ys), interleave_all(xss) | alternate elements, round-robin over many lists |
intersperse(xs, sep), intercalate(xss, sep) | separators between elements or lists |
zip3, zip_with, zip_longest, zip_longest_opt | zips of three lists, with a function, or to the longer length |
unzip(ps), unzip3(ts) | split pairs and triples |
transpose(rows) | rows to columns, ragged rows allowed |
pairwise(xs) | adjacent pairs |
| Function | What it does |
|---|---|
cartesian(xs, ys), cartesian_all(xss) | cartesian products |
permutations(xs, k), combinations(xs, k), combinations_with_replacement(xs, k) | in lexicographic order of positions, as in itertools |
power_set(xs) | every sublist, by size; at most 20 elements |
count_permutations(n, k), count_combinations(n, k) | exact counts, none when they don't fit an Int |
combinatorics_limit() | the output limit, 1,000,000 |
| Function | What it does |
|---|---|
replicate(x, n), cycle(xs, n), cycle_take(xs, n) | repetition |
map_indexed(xs, f), filter_indexed(xs, p) | with the index |
filter_map(xs, f), compact(xs), find_map(xs, f) | working with Opt |
partition_map(xs, f), partition_results(rs) | split oks from errs |
sequence_opt(xs), sequence_res(rs) | all or nothing |
| Function | What it does |
|---|---|
at(xs, i) | element with negative indexes from the end |
insert_at, insert_all_at | insert, the index clamped |
remove_at, remove_first | remove by index or the first equal element |
set_at, update_at | replace one element |
swap(xs, i, j), move_item(xs, from, to) | reorder |
| Function | What it does |
|---|---|
find_last, find_last_index, last_index_of | search from the end |
indices_where(xs, p), indices_of(xs, x) | every matching index |
partition_point(xs, p) | binary search for the end of the p prefix |
lower_bound_by, upper_bound_by, binary_search_by | binary search by key |
insert_sorted(xs, x), insert_sorted_by(xs, x, key) | stable sorted insertion |
index_of_sublist, last_index_of_sublist, contains_sublist | contiguous sublists |
has_prefix, has_suffix | starts_with and ends_with for lists |
count_sublist, replace_sublist | non-overlapping occurrences, left to right |
| Function | What it does |
|---|---|
fold_right, reduce, reduce_right, scan_right | right folds and folds without a start value |
is_sorted, is_strictly_sorted, is_sorted_by, is_sorted_with | sortedness by Ord, key or comparator |
argsort, argsort_by, ranks, dense_ranks | sorting permutations and ranks |
compare_lists(xs, ys) | lexicographic comparison |
merge_by(xs, ys, key), merge_all(xss) | stable merges of sorted lists |
Functions with an Ord bound use the type's Ord impl, so a hand-written order (a descending wrapper, say) is respected.
| Function | What it does |
|---|---|
union, intersect, difference, sym_difference | distinct results in first-seen order |
contains_all, contains_any, is_disjoint | membership |
multi_intersect, multi_difference, multi_union | keeping duplicates by count, in input order |
is_permutation(xs, ys) | the same elements with the same counts |
| Function | What it does |
|---|---|
sample(xs, k, seed), sample_ordered(xs, k, seed) | k distinct positions, pure in the seed like the builtin shuffle |
View (xs.view, iota, iterate, take_while, chain) and generators (yield) cover them, so everything here is eager.chunks, windows, partition, scan, flatten, take_while, drop_while, rotate, binary_search, lower_bound, upper_bound, group_by, counts, merge, next_perm, unique, shuffle, choice, slice, find_index, index_of, zip, enumerate.nth_element, in-place sorts): SSPUR lists are immutable values.rand_weighted and Rng.Performance notes: index-based results are built with map, filter and flat_map, so they are linear in both tiers. index_of_sublist and friends are naive (O(n * m) in the worst case). In the interpreter, tails and inits copy each suffix and prefix.
sspur add lists
MIT licensed.
fn split_at[A](xs: List[A], i: Int) -> (List[A], List[A])(xs.take(i), xs.drop(i)) with i clamped to 0..=xs.len: a negative i gives ([], xs) and an i past the end gives (xs, []).
fn split_while[A, e](xs: List[A], p: A -> Bool ! e) -> (List[A], List[A]) ! eThe longest prefix whose elements all satisfy p, and the rest (Haskell's span). p is called once per element of the prefix, plus once for the first element that fails.
fn split_until[A, e](xs: List[A], p: A -> Bool ! e) -> (List[A], List[A]) ! eThe longest prefix with no element satisfying p, and the rest, which starts with the first element that does (Haskell's break).
fn split_when[A, e](xs: List[A], p: (A, A) -> Bool ! e) -> List[List[A]] ! eSplits xs between every pair of neighbours a, b for which p(a, b) holds; no element is lost. An empty list gives []. split_when([1, 2, 4, 5], (a, b) => b != a + 1) is [[1, 2], [4, 5]].
fn split_on[A](xs: List[A], sep: A) -> List[List[A]]Splits xs at every element equal to sep, dropping the separators, like Str.split: [] gives [[]], and neighbouring or trailing separators give empty pieces, so the result always has one more piece than there are separators.
fn split_where[A, e](xs: List[A], p: A -> Bool ! e) -> List[List[A]] ! esplit_on with a predicate: splits at every element satisfying p and drops those elements.
fn split_parts[A](xs: List[A], n: Int where _ >= 1) -> List[List[A]]Exactly n consecutive parts whose lengths differ by at most one, the longer parts first (more-itertools' divide). With fewer than n elements the last parts are empty.
fn sliding[A](xs: List[A], size: Int where _ >= 1, step: Int where _ >= 1, partial: Bool) -> List[List[A]]Windows of size elements starting every step elements (Kotlin's windowed). With partial false only full windows are returned; with partial true the windows that start before the end but run past it are kept, shortened. sliding(xs, n, 1, false) is xs.windows(n) and sliding(xs, n, n, true) is xs.chunks(n).
fn chunks_padded[A](xs: List[A], n: Int where _ >= 1, fill: A) -> List[List[A]]xs.chunks(n) with the last chunk padded with fill to n elements. An empty list gives [].
fn chunk_by[A, K, e](xs: List[A], key: A -> K ! e) -> List[(K, List[A])] ! eGroups runs of consecutive elements with the same key, each with its key (Python's itertools.groupby, Rust's chunk_by). key is called once per element. Equal keys that are not adjacent make separate runs; use group_into to group them all.
fn rle[A](xs: List[A]) -> List[(A, Int)]Run-length encoding: each run of equal neighbours as (value, length).
fn rle_decode[A](runs: List[(A, Int)]) -> List[A]The inverse of rle; counts below 1 contribute nothing.
fn tails[A](xs: List[A]) -> List[List[A]]Every suffix of xs, longest first, ending with []: xs.len + 1 lists.
fn inits[A](xs: List[A]) -> List[List[A]]Every prefix of xs, shortest first, starting with []: xs.len + 1 lists.
fn take_last[A](xs: List[A], n: Int) -> List[A]The last n elements (all of them when n >= xs.len, none when n <= 0).
fn drop_last[A](xs: List[A], n: Int) -> List[A]xs without its last n elements ([] when n >= xs.len, xs when n <= 0).
fn take_last_while[A, e](xs: List[A], p: A -> Bool ! e) -> List[A] ! eThe longest suffix whose elements all satisfy p, tested from the end.
fn drop_last_while[A, e](xs: List[A], p: A -> Bool ! e) -> List[A] ! exs without the longest suffix whose elements all satisfy p (an rstrip for lists).
fn pad_to[A](xs: List[A], n: Int, fill: A) -> List[A]xs padded at the end with fill to at least n elements; longer lists are unchanged.
fn pad_front_to[A](xs: List[A], n: Int, fill: A) -> List[A]xs padded at the front with fill to at least n elements; longer lists are unchanged.
fn dedup[A](xs: List[A]) -> List[A]xs with runs of equal neighbours collapsed to their first element (Unix uniq). Use the builtin unique to drop every repeat.
fn dedup_by[A, K, e](xs: List[A], key: A -> K ! e) -> List[A] ! ededup comparing keys: keeps the first element of each run of neighbours with equal keys.
fn distinct_by[A, K, e](xs: List[A], key: A -> K ! e) -> List[A] ! eThe first element for each distinct key, in input order (lodash's uniqBy). key is called once per element.
fn duplicates[A](xs: List[A]) -> List[A]The values that occur more than once, each once, in the order of their first occurrence.
fn all_equal[A](xs: List[A]) -> BoolWhether every element equals the first (true for an empty list).
fn all_distinct[A](xs: List[A]) -> BoolWhether no two elements are equal (true for an empty list).
fn single[A](xs: List[A]) -> Opt[A]The only element of xs, or none when it has zero or several.
fn frequencies[A](xs: List[A]) -> Map[A, Int]How many times each value occurs (Python's Counter). The builtin counts gives the same numbers as a list in first-seen order.
fn count_by[A, K, e](xs: List[A], key: A -> K ! e) -> Map[K, Int] ! eHow many elements have each key (lodash's countBy).
fn count_where[A, e](xs: List[A], p: A -> Bool ! e) -> Int ! eHow many elements satisfy p.
fn index_by[A, K, e](xs: List[A], key: A -> K ! e) -> Map[K, A] ! eA map from each element's key to the element (lodash's keyBy). When several elements share a key the last one wins; use group_into to keep them all.
fn associate[A, K, V, e](xs: List[A], f: A -> (K, V) ! e) -> Map[K, V] ! eA map built from the (key, value) pair f gives for each element (Kotlin's associate). When keys repeat the last pair wins.
fn group_into[A, K, e](xs: List[A], key: A -> K ! e) -> Map[K, List[A]] ! eThe elements grouped by key into a map; each group keeps input order. The builtin group_by gives the same groups as a list in first-seen key order.
fn sum_by[A, e](xs: List[A], f: A -> Int ! e) -> Int ! eThe sum of f over the elements (0 for an empty list); overflow traps like +.
fn sum_by_f64[A, e](xs: List[A], f: A -> F64 ! e) -> F64 ! eThe sum of f over the elements as floats (0.0 for an empty list), with Kahan-Babuska compensation, so long sums of small terms lose no precision to a large running total.
fn min_by[A, K: Ord, e](xs: List[A], key: A -> K ! e) -> Opt[A] ! eThe first element with the smallest key, or none for an empty list. key is called once per element.
fn max_by[A, K: Ord, e](xs: List[A], key: A -> K ! e) -> Opt[A] ! eThe first element with the largest key, or none for an empty list. key is called once per element.
fn min_max[T: Ord](xs: List[T]) -> Opt[(T, T)]The smallest and the largest element in one pass (the first of each among equals), or none for an empty list.
fn min_max_by[A, K: Ord, e](xs: List[A], key: A -> K ! e) -> Opt[(A, A)] ! emin_by and max_by in one pass: the first element with the smallest key and the first with the largest, or none for an empty list.
fn top_k[A, K: Ord, e](xs: List[A], k: Int, key: A -> K ! e) -> List[A] ! eThe k elements with the largest keys, largest first; equal keys keep input order, so the result is stable. k <= 0 gives [] and k >= xs.len sorts the whole list.
fn bottom_k[A, K: Ord, e](xs: List[A], k: Int, key: A -> K ! e) -> List[A] ! eThe k elements with the smallest keys, smallest first, stable like top_k.
fn largest[T: Ord](xs: List[T], k: Int) -> List[T]The k largest elements, largest first (Python's heapq.nlargest).
fn smallest[T: Ord](xs: List[T], k: Int) -> List[T]The k smallest elements, smallest first (Python's heapq.nsmallest).
fn interleave[A](xs: List[A], ys: List[A]) -> List[A]Alternates elements of xs and ys, starting with xs; the rest of the longer list follows.
fn interleave_all[A](xss: List[List[A]]) -> List[A]Round-robin over any number of lists: the first element of each, then the second of each, skipping lists that have run out (more-itertools' interleave_longest).
fn intersperse[A](xs: List[A], sep: A) -> List[A]sep between every two elements: [a, sep, b, sep, c].
fn intercalate[A](xss: List[List[A]], sep: List[A]) -> List[A]The lists of xss joined with sep between every two (join for lists of lists).
fn zip3[A, B, C](xs: List[A], ys: List[B], zs: List[C]) -> List[(A, B, C)]Triples of the elements at the same index, as long as the shortest list.
fn zip_with[A, B, C, e](xs: List[A], ys: List[B], f: (A, B) -> C ! e) -> List[C] ! ef applied to the elements at the same index, as long as the shorter list.
fn zip_longest[A, B](xs: List[A], ys: List[B], fill_a: A, fill_b: B) -> List[(A, B)]Pairs as long as the longer list, the shorter one padded with its fill value (Python's zip_longest).
fn zip_longest_opt[A, B](xs: List[A], ys: List[B]) -> List[(Opt[A], Opt[B])]Pairs as long as the longer list, with none where a list has run out.
fn unzip[A, B](ps: List[(A, B)]) -> (List[A], List[B])The firsts and the seconds of a list of pairs.
fn unzip3[A, B, C](ts: List[(A, B, C)]) -> (List[A], List[B], List[C])The three components of a list of triples.
fn transpose[A](rows: List[List[A]]) -> List[List[A]]Rows become columns. Ragged input is allowed: column i holds element i of every row that has one, in row order, so it is shorter than the row count when some rows are short, and there are as many columns as the longest row has elements. [] gives [].
fn pairwise[A](xs: List[A]) -> List[(A, A)]Adjacent pairs: [(x0, x1), (x1, x2), ...], empty for fewer than two elements.
fn combinatorics_limit() -> IntThe most lists or pairs the combinatoric functions return: 1,000,000. Their pre clauses reject inputs whose output would be larger, so a typo can't ask for a trillion permutations.
fn cartesian[A, B](xs: List[A], ys: List[B]) -> List[(A, B)]Every pair (x, y) with x from xs and y from ys, x varying slowest. At most combinatorics_limit() pairs.
fn cartesian_all[A](xss: List[List[A]]) -> List[List[A]]The n-ary cartesian product (Python's itertools.product): every list taking one element from each list of xss, the last varying fastest. [] gives [[]], and any empty list gives []. At most combinatorics_limit() lists.
fn permutations[A](xs: List[A], k: Int where _ >= 0) -> List[List[A]]The k-permutations of xs by position, in lexicographic order of positions (Python's itertools.permutations(xs, k)): n! / (n - k)! lists, [[]] for k == 0 and [] for k > xs.len. Equal elements are treated as distinct. At most combinatorics_limit() lists.
fn combinations[A](xs: List[A], k: Int where _ >= 0) -> List[List[A]]The k-combinations of xs by position, in lexicographic order of positions, each in input order (Python's itertools.combinations): C(n, k) lists, [[]] for k == 0 and [] for k > xs.len. At most combinatorics_limit() lists.
fn combinations_with_replacement[A](xs: List[A], k: Int where _ >= 0) -> List[List[A]]The k-combinations with repetition (Python's combinations_with_replacement): C(n + k - 1, k) lists in lexicographic order of positions; [[]] for k == 0, [] for an empty xs and k > 0. At most combinatorics_limit() lists.
fn power_set[A](xs: List[A]) -> List[List[A]]Every sublist of xs (keeping input order), by size and then lexicographically by position: 2 ** xs.len lists, starting with []. At most 20 elements (1,048,576 lists).
fn count_permutations(n: Int, k: Int) -> Opt[Int]The number of k-permutations of n items, n! / (n - k)! (0 when k > n), or none when it doesn't fit in an Int or an argument is negative.
fn count_combinations(n: Int, k: Int) -> Opt[Int]The number of k-combinations of n items, the binomial coefficient (0 when k > n), or none when it doesn't fit in an Int or an argument is negative.
fn replicate[A](x: A, n: Int) -> List[A]A list of n copies of x ([] when n <= 0).
fn cycle[A](xs: List[A], n: Int) -> List[A]xs repeated n times ([] when n <= 0).
fn cycle_take[A](xs: List[A], n: Int) -> List[A]The first n elements of xs repeated forever ([] when xs is empty or n <= 0).
fn map_indexed[A, B, e](xs: List[A], f: (Int, A) -> B ! e) -> List[B] ! ef(i, x) for each element and its index.
fn filter_indexed[A, e](xs: List[A], p: (Int, A) -> Bool ! e) -> List[A] ! eThe elements for which p(i, x) holds, with i the index.
fn filter_map[A, B, e](xs: List[A], f: A -> Opt[B] ! e) -> List[B] ! eThe values of f that are some, in order (Rust's filter_map).
fn compact[A](xs: List[Opt[A]]) -> List[A]The values inside the somes, in order.
fn find_map[A, B, e](xs: List[A], f: A -> Opt[B] ! e) -> Opt[B] ! eThe first some that f gives, calling f only until then (Rust's find_map).
fn partition_map[A, B, C, e](xs: List[A], f: A -> Res[B, C] ! e) -> (List[B], List[C]) ! eThe ok values and the err values of f, each in order.
fn partition_results[B, C](rs: List[Res[B, C]]) -> (List[B], List[C])The ok values and the err values, each in order.
fn sequence_opt[A](xs: List[Opt[A]]) -> Opt[List[A]]some of every value when all are some, else none.
fn sequence_res[A, E](rs: List[Res[A, E]]) -> Res[List[A], E]ok of every value when all are ok, else the first err.
fn at[A](xs: List[A], i: Int) -> Opt[A]The element at i, counting from the end when i is negative (-1 is the last), or none when out of range.
fn insert_at[A](xs: List[A], i: Int, x: A) -> List[A]xs with x inserted before index i; i is clamped to 0..=xs.len, so a negative i inserts at the front and one past the end appends.
fn insert_all_at[A](xs: List[A], i: Int, ys: List[A]) -> List[A]xs with all of ys inserted before index i, clamped like insert_at.
fn remove_at[A](xs: List[A], i: Int) -> List[A]xs without the element at i; an out-of-range i (negative included) leaves it unchanged.
fn remove_first[A](xs: List[A], x: A) -> List[A]xs without the first element equal to x (unchanged when there is none). The builtin filter removes them all.
fn set_at[A](xs: List[A], i: Int, x: A) -> List[A]xs with the element at i replaced by x; out of range leaves it unchanged.
fn update_at[A, e](xs: List[A], i: Int, f: A -> A ! e) -> List[A] ! exs with the element at i replaced by f of it; out of range leaves it unchanged and doesn't call f.
fn swap[A](xs: List[A], i: Int, j: Int) -> List[A]xs with the elements at i and j exchanged; unchanged when either is out of range.
fn move_item[A](xs: List[A], from: Int, to: Int) -> List[A]xs with the element at from moved so that it ends at index to (clamped to the valid range); unchanged when from is out of range.
fn find_last[A, e](xs: List[A], p: A -> Bool ! e) -> Opt[A] ! eThe last element satisfying p, testing from the end.
fn find_last_index[A, e](xs: List[A], p: A -> Bool ! e) -> Opt[Int] ! eThe index of the last element satisfying p, testing from the end.
fn last_index_of[A](xs: List[A], x: A) -> Opt[Int]The index of the last element equal to x.
fn indices_where[A, e](xs: List[A], p: A -> Bool ! e) -> List[Int] ! eThe indexes of every element satisfying p, ascending.
fn indices_of[A](xs: List[A], x: A) -> List[Int]The indexes of every element equal to x, ascending.
fn partition_point[A, e](xs: List[A], p: A -> Bool ! e) -> Int ! eThe first index at which p is false, for a list that is partitioned (every element satisfying p comes before every one that doesn't); xs.len when p holds everywhere. Binary search: p is called O(log n) times.
fn lower_bound_by[A, K: Ord, e](xs: List[A], key: A -> K ! e, target: K) -> Int ! eThe first index whose key is at least target, in a list sorted by key (xs.len if none).
fn upper_bound_by[A, K: Ord, e](xs: List[A], key: A -> K ! e, target: K) -> Int ! eThe first index whose key is greater than target, in a list sorted by key (xs.len if none).
fn binary_search_by[A, K: Ord, e](xs: List[A], key: A -> K ! e, target: K) -> Opt[Int] ! eThe first index whose key equals target in a list sorted by key, or none.
fn insert_sorted[T: Ord](xs: List[T], x: T) -> List[T]x inserted into the sorted list xs after any equal elements, so the list stays sorted and insertion is stable.
fn insert_sorted_by[A, K: Ord, e](xs: List[A], x: A, key: A -> K ! e) -> List[A] ! ex inserted into xs, sorted by key, after any elements with an equal key.
fn index_of_sublist[A](xs: List[A], sub: List[A]) -> Opt[Int]The first index at which sub occurs in xs as a contiguous run; some(0) for an empty sub. Worst case O(xs.len * sub.len).
fn last_index_of_sublist[A](xs: List[A], sub: List[A]) -> Opt[Int]The last index at which sub occurs in xs; some(xs.len) for an empty sub.
fn contains_sublist[A](xs: List[A], sub: List[A]) -> BoolWhether sub occurs in xs as a contiguous run (always for an empty sub).
fn has_prefix[A](xs: List[A], prefix: List[A]) -> BoolWhether xs starts with prefix.
fn has_suffix[A](xs: List[A], suffix: List[A]) -> BoolWhether xs ends with suffix.
fn count_sublist[A](xs: List[A], sub: List[A]) -> IntHow many times sub occurs in xs without overlapping, scanning left to right (like Str.count in Python). An empty sub counts xs.len + 1 times.
fn replace_sublist[A](xs: List[A], sub: List[A], by: List[A]) -> List[A]xs with every non-overlapping occurrence of sub (left to right) replaced by by. An empty sub leaves xs unchanged.
fn fold_right[A, B, e](xs: List[A], init: B, f: (A, B) -> B ! e) -> B ! eFolds from the right: f(x0, f(x1, ... f(xn, init))).
fn reduce[A, e](xs: List[A], f: (A, A) -> A ! e) -> Opt[A] ! eFolds from the left with the first element as the start, or none for an empty list.
fn reduce_right[A, e](xs: List[A], f: (A, A) -> A ! e) -> Opt[A] ! eFolds from the right with the last element as the start, or none for an empty list.
fn scan_right[A, B, e](xs: List[A], init: B, f: (A, B) -> B ! e) -> List[B] ! eThe accumulators of fold_right, aligned with the elements: element i of the result is f(xs[i], result[i + 1]), the last one f(xs[n - 1], init). Same length as xs, like the builtin scan.
fn is_sorted[T: Ord](xs: List[T]) -> BoolWhether every element is at most the next one.
fn is_strictly_sorted[T: Ord](xs: List[T]) -> BoolWhether every element is less than the next one (sorted, no repeats).
fn is_sorted_by[A, K: Ord, e](xs: List[A], key: A -> K ! e) -> Bool ! eWhether the keys never decrease. key is called once per element.
fn is_sorted_with[A, e](xs: List[A], cmp: (A, A) -> Int ! e) -> Bool ! eWhether cmp(a, b) <= 0 for every pair of neighbours, with a comparator like sort_with's.
fn argsort[T: Ord](xs: List[T]) -> List[Int]The indexes that sort xs (stable: equal elements keep input order), NumPy's argsort. argsort(xs).map(i => xs[i]) is xs sorted.
fn argsort_by[A, K: Ord, e](xs: List[A], key: A -> K ! e) -> List[Int] ! eThe indexes that sort xs by key, stable. key is called once per element.
fn ranks[T: Ord](xs: List[T]) -> List[Int]The 0-based position of each element in the stable sort (ordinal ranks: ties get consecutive ranks in input order). The inverse permutation of argsort.
fn dense_ranks[T: Ord](xs: List[T]) -> List[Int]Dense ranks: 0 for the smallest value, and one more for each larger distinct value; equal elements share a rank.
fn compare_lists[T: Ord](xs: List[T], ys: List[T]) -> IntLexicographic comparison by the elements' Ord: -1, 0 or 1. A proper prefix comes first.
fn merge_by[A, K: Ord, e](xs: List[A], ys: List[A], key: A -> K ! e) -> List[A] ! eThe elements of the sorted lists xs and ys in one sorted list, by key; on equal keys elements of xs come first. The builtin merge does this for the elements themselves.
fn merge_all[T: Ord](xss: List[List[T]]) -> List[T]A k-way merge: every element of the lists in one sorted list, stable (on equals, earlier lists first). With sorted inputs this is the merge; any input gives a sorted result.
fn union[A](xs: List[A], ys: List[A]) -> List[A]The distinct elements of xs and then of ys that are not in xs, each once, in first-seen order.
fn intersect[A](xs: List[A], ys: List[A]) -> List[A]The distinct elements of xs that also occur in ys, in xs order.
fn difference[A](xs: List[A], ys: List[A]) -> List[A]The distinct elements of xs that don't occur in ys, in xs order.
fn sym_difference[A](xs: List[A], ys: List[A]) -> List[A]The distinct elements in exactly one of the lists: those of xs first, then those of ys.
fn contains_all[A](xs: List[A], ys: List[A]) -> BoolWhether every element of ys occurs in xs (true for an empty ys).
fn contains_any[A](xs: List[A], ys: List[A]) -> BoolWhether some element of ys occurs in xs (false for an empty ys).
fn is_disjoint[A](xs: List[A], ys: List[A]) -> BoolWhether the lists have no element in common.
fn multi_intersect[A](xs: List[A], ys: List[A]) -> List[A]The multiset intersection, keeping duplicates: each value as many times as it occurs in both lists (the smaller count), in xs order.
fn multi_difference[A](xs: List[A], ys: List[A]) -> List[A]The multiset difference: xs with one occurrence removed for each occurrence in ys, the earliest first (Python's Counter subtraction, keeping order).
fn multi_union[A](xs: List[A], ys: List[A]) -> List[A]The multiset union: all of xs, then the occurrences in ys beyond those in xs (each value as many times as the larger count).
fn is_permutation[A](xs: List[A], ys: List[A]) -> BoolWhether ys is a permutation of xs: the same elements with the same counts.
fn sample[A](xs: List[A], k: Int, seed: Int) -> List[A]k distinct positions of xs chosen uniformly at random, in random order (Python's random.sample), as a pure function of seed like the builtin shuffle. k is clamped to 0..=xs.len.
fn sample_ordered[A](xs: List[A], k: Int, seed: Int) -> List[A]sample, with the chosen elements in their input order.
| Version | Published | Commit | Source | Effects |
|---|---|---|---|---|
| 0.1.0 | 2026-10-16 | 357044583c | snapshot |