std.collections
API summary (generated from the Beans source by npm run coverage): 11 package functions · 4 types · 4 constructors · 59 instance methods.
std.collections is two things: four container types the builtins do not
cover — Deque, Set, SortedMap and PriorityQueue — and a set of generic
helper functions over the builtin List and Map. All of it is plain Beans.
Read the source under
stdlib/std/collections/.
import std.collectionsSome functions are generic and use trait bounds like T implements Eq. That
means the element type must support that trait (for example, Eq for equality,
Hash to be a map key, Clone to be copied). The builtin types you normally use
already support these.
A queue open at both ends. List.insert(0, v) and List.remove(0) shift every
other element, so a list used as a queue is quadratic; this is not. Both ends
push and pop in constant time.
pub class Deque<T implements Clone>
new Deque()
pub fn push_front(value: T)pub fn push_back(value: T)pub fn pop_front() -> Option<T>pub fn pop_back() -> Option<T>pub fn first() -> Option<T>pub fn last() -> Option<T>pub fn get(index: int) -> Option<T>pub fn len() -> intpub fn is_empty() -> boolpub fn clear()pub fn to_list() -> List<T>push_front/push_backadd at either end;pop_front/pop_backtake from either end and answernonewhen the deque is empty.firstandlastlook without removing.get(index)indexes from the front,0being the same elementfirst()answers, and givesnoneout of range.to_listcopies the elements out front-to-back and leaves the deque alone.
Reach for it when the front is a working end too: a work queue, a sliding window, a breadth-first walk, an undo/redo pair.
import std.ioimport std.collections
fn main() { var line: collections.Deque<string> = new collections.Deque<string>() line.push_back("second") line.push_front("first") line.push_back("third") io.println(line.len()) // 3 io.println(line.pop_front().or("-")) // first io.println(line.last().or("-")) // third io.println(line.to_list()) // [second, third]}A collection of unique values, with O(1) add, membership, and removal. Set operations walk the storage without first copying keys into lists.
pub class Set<T implements Eq & Hash & Clone>
new Set()
pub fn add(value: T) -> boolpub fn add_all(values: List<T>) -> intpub fn contains(value: T) -> boolpub fn remove(value: T) -> boolpub fn len() -> intpub fn is_empty() -> boolpub fn clear()pub fn reserve(capacity: int)pub fn items() -> List<T>pub fn union_with(other: Set<T>) -> Set<T>pub fn intersection(other: Set<T>) -> Set<T>pub fn difference(other: Set<T>) -> Set<T>pub fn symmetric_difference(other: Set<T>) -> Set<T>pub fn is_subset_of(other: Set<T>) -> boolpub fn is_superset_of(other: Set<T>) -> boolpub fn is_disjoint_from(other: Set<T>) -> boolpub fn equals(other: Set<T>) -> booladdanswers true when the value was new, so you can count what you actually added.add_allreturns how many of the list were new.removeanswers true when the value was there.reservesizes the storage up front.itemscopies the members out; the order is the storage’s, not an order you should rely on.- The four algebra methods each build a new set and leave both inputs alone.
union_withis spelled that way becauseunionis a keyword. is_subset_of,is_superset_of,is_disjoint_fromandequalsanswer without building anything.
import std.ioimport std.collections
fn main() { var left: collections.Set<int> = new collections.Set<int>() left.add_all([1, 2, 3]) var right: collections.Set<int> = new collections.Set<int>() right.add_all([3, 4]) io.println(left.contains(2)) // true io.println(left.intersection(right).items()) // [3] io.println(left.is_disjoint_from(right)) // false}SortedMap
Section titled “SortedMap”SortedMap keeps keys sorted and supports neighbor, rank, and range queries:
find the next key, count keys below a value, or get all keys in a range.
Updates preserve the key order.
pub class SortedMap<K implements Order & Clone, V implements Clone>
new SortedMap()
pub fn set(key: K, value: V)pub fn insert(key: K, value: V) -> boolpub fn get(key: K) -> Option<V>pub fn contains_key(key: K) -> boolpub fn remove(key: K) -> boolpub fn len() -> intpub fn is_empty() -> boolpub fn clear()pub fn keys() -> List<K>pub fn values() -> List<V>pub fn first_key() -> Option<K>pub fn last_key() -> Option<K>pub fn first_value() -> Option<V>pub fn last_value() -> Option<V>pub fn floor_key(key: K) -> Option<K>pub fn ceiling_key(key: K) -> Option<K>pub fn lower_key(key: K) -> Option<K>pub fn higher_key(key: K) -> Option<K>pub fn rank(key: K) -> intpub fn key_at(index: int) -> Option<K>pub fn value_at(index: int) -> Option<V>pub fn range_keys(from: K, to: K) -> List<K>pub fn range_values(from: K, to: K) -> List<V>pub fn range_count(from: K, to: K) -> intsetwrites whatever is there;insertonly writes when the key is new and answers whether it did.removeanswers true when the key was there.keysandvaluescome back in key order, and every other reader below is in that order too.- The four neighbour queries differ only in whether an exact match counts:
floor_keyis the largest key<= key,ceiling_keythe smallest>= key,lower_keythe largest strictly<,higher_keythe smallest strictly>. rank(key)is how many keys sort before it — so it is where the key would go, whether or not it is present.key_at/value_atare the inverse, indexing by position in key order.- The three range readers take a half-open
[from, to).range_countanswers without building a list.
import std.ioimport std.collections
fn main() { var scores: collections.SortedMap<int, string> = new collections.SortedMap<int, string>() scores.set(30, "carol") scores.set(10, "alice") scores.set(20, "bob") io.println(scores.keys()) // [10, 20, 30] io.println(scores.first_key().or(0)) // 10 io.println(scores.floor_key(25).or(0)) // 20 io.println(scores.rank(25)) // 2 io.println(scores.range_keys(10, 30)) // [10, 20]}PriorityQueue
Section titled “PriorityQueue”A binary min-heap with a priority stored separately from each value. The value with the smallest priority comes out first. Insertion takes O(log n), compared with O(n) for a sorted list. Use it for scheduling, expiry queues, or a Dijkstra frontier.
pub class PriorityQueue<P implements Order & Clone, V implements Clone>
new PriorityQueue()
pub fn push(priority: P, value: V)pub fn peek() -> Option<V>pub fn peek_priority() -> Option<P>pub fn pop() -> Option<V>pub fn len() -> intpub fn is_empty() -> boolpub fn clear()pushadds a value under a priority. Priorities need not be unique, and two equal priorities come out in no guaranteed order relative to each other.peekreads the next value without removing it,peek_priorityreads its priority — useful for “is the earliest deadline due yet” without popping.popremoves and answers the next value, ornonewhen empty.
import std.ioimport std.collections
fn main() { var work: collections.PriorityQueue<int, string> = new collections.PriorityQueue<int, string>() work.push(30, "later") work.push(10, "urgent") work.push(20, "soon") io.println(work.peek_priority().or(0)) // 10 io.println(work.pop().or("-")) // urgent io.println(work.pop().or("-")) // soon io.println(work.len()) // 1}List helpers
Section titled “List helpers”pub fn sum_int(values: List<int>) -> intpub fn frequencies(values: List<string>) -> Map<string, int>pub fn count<T implements Eq>(values: List<T>, needle: T) -> intpub fn filter<T implements Clone>(values: List<T>, keep: fn(T) -> bool) -> List<T>pub fn transform<T implements Clone, U>(values: List<T>, apply: fn(T) -> U) -> List<U>pub fn unique<T implements Eq & Hash & Clone>(values: List<T>) -> List<T>sum_intadds up a list of ints.frequenciescounts how often each string appears.countcounts how many items equalneedle.filterkeeps the items for whichkeepreturns true.transformmakes a new list by applyingapplyto each item.uniquedrops duplicates, keeping first-seen order.
import std.ioimport std.collections
fn main() { let nums: List<int> = [3, 1, 3, 2, 1] io.println(collections.sum_int(nums)) // 10 io.println(collections.count(nums, 3)) // 2 let odds: List<int> = collections.filter(nums, fn(n: int) -> bool { return n % 2 == 1 }) io.println(odds) // [3, 1, 3, 1] io.println(collections.unique(nums)) // [3, 1, 2]}Map helpers
Section titled “Map helpers”Several of these take inout maps. inout means the function changes the
caller’s own map in place; you do not get a copy back. After the call, your map
holds the new state.
pub fn increment<K implements Eq & Hash>(inout values: Map<K, int>, key: K, delta: int) -> intpub fn get_or_insert_with<K implements Eq & Hash, V implements Clone>(inout values: Map<K, V>, key: K, make: fn() -> V) -> Vpub fn merge_with<K implements Eq & Hash & Clone, V implements Clone>(inout target: Map<K, V>, source: Map<K, V>, combine: fn(V, V) -> V)pub fn remove_if<K implements Eq & Hash & Clone, V implements Clone>(inout values: Map<K, V>, remove: fn(K, V) -> bool) -> intpub fn map_values_with_key<K implements Eq & Hash & Clone, V implements Clone, U>(values: Map<K, V>, apply: fn(K, V) -> U) -> Map<K, U>incrementaddsdeltatokey’s value, starting from 0 for a missing key, and returns the new count.get_or_insert_withreturns the value atkey, or inserts whatmake()gives and returns that.merge_withfoldssourceintotarget. On a key that exists in both, it keepscombine(old, new).remove_ifdrops each entry whereremovereturns true and returns how many were dropped.map_values_with_keybuilds a new map with the same keys and values taken fromapply(key, value).
import std.ioimport std.collections
fn main() { var counts: Map<string, int> = {} collections.increment(inout counts, "a", 1) collections.increment(inout counts, "a", 2) // counts["a"] is now 3 io.println(counts.get("a")) // some(3)}See also
Section titled “See also”- Collections, the builtin
ListandMaptypes these helpers work on.