Enumerableprelude
trait Foldable
Traversal operations that every foldable collection gets for free.
A type becomes Foldable by implementing one method, reduce; the rest: each, all?, any?, find, count: are derived from it. List, String, Map, Range and both flavours of Set all implement it, so these methods read the same whatever you point them at.
[1, 2, 3].all? { |n| n > 0 } # => true
"hello".any?(~digit?) # => false
{ a: 1, b: 2 }.count { |k, v| v > 1 } # => 1
Blocks are applied via Kex.Intrinsic.Fun.applyItem, which auto-splats a pair item into a two-argument block. That is what lets a Map traversal be written { |key, value| ... } even though the fold hands over one tuple.
reduce
Folds the collection from the left. The one operation a Foldable type must define; everything else here is written in terms of it.
reduce : A -> (A -> T -> A) -> A
Parameters
accA- the initial accumulator
fA -> T -> A- combines the accumulator with each item
Returns: A — the final accumulator
Examples
[1, 2, 3].reduce(0) { |sum, n| sum + n } # => 6
each
Calls f with each item, for its side effects.
The loop of last resort, when what you want is a new collection rather than an effect, map or filter says so more clearly.
each(f)
Parameters
fT -> Void- called once per item
Returns: Void —
Examples
["ada", "grace"].each { |name| IO.printLine(name) }
Over a map, the block takes key and value
{ host: "localhost", port: 8080 }.each do |key, value|
IO.printLine("${key} = ${value}")
end
eachIndexed
Calls f with each item and its 0-based position, for its side effects.
The index is the LAST block parameter, so a Map entry can be taken either whole (|entry, i|) or spread (|k, v, i|).
eachIndexed(f)
Parameters
fT -> Integer -> Void- called with each item and its index
Returns: Void —
Examples
["a", "b"].eachIndexed { |s, i| IO.printLine("${i}: ${s}") }
# prints: 0: a
# 1: b
Numbering the lines of a file
text.lines.eachIndexed { |line, i| IO.printLine("${i + 1}\t${line}") }
all?
Returns true when every item satisfies pred. An empty collection answers true.
all?(pred)
Parameters
predT -> Bool- the test applied to each item
Returns: Bool — true when every item matches
Examples
[2, 4].all? { |n| n.even? } # => true
[2, 5].all? { |n| n.even? } # => false
[].all? { |n| n.even? } # => true
Validating every field of a form
fields.all? { |name, value| !value.blank? }
any?
Returns true when at least one item satisfies pred. An empty collection answers false.
any?(pred)
Parameters
predT -> Bool- the test applied to each item
Returns: Bool — true when any item matches
Examples
[1, 2, 3].any? { |n| n > 2 } # => true
[1, 2, 3].any? { |n| n > 9 } # => false
Detecting a flag among arguments
args.any? { |a| a == "--verbose" }
find
Returns the first item satisfying pred, or None when nothing does.
find(pred)
Parameters
predT -> Bool- the test applied to each item
Returns: T? — the first match, or None
Examples
[1, 2, 3].find { |n| n > 1 } # => Just(2)
[1, 2, 3].find { |n| n > 9 } # => None
Looking a record up by one of its fields
users.find { |u| u.email == target }.map { |u| u.name }.or("unknown")
count
Returns how many items satisfy pred.
count(pred)
Parameters
predT -> Bool- the test applied to each item
Returns: Integer — the number of matches
Examples
[1, 2, 3, 4].count { |n| n.even? } # => 2
How many lines are comments
text.lines.count { |line| line.trim.startsWith?("#") }
trait Enumerable
Collection-producing operations that every foldable collection gets for free.
Like Foldable, a type joins by implementing reduce alone. The defaults here answer with a list, because the block may return anything at all; a type that can do better overrides them: Map.filter gives back a map, Set.map gives back a set, String.filter gives back a string.
[1, 2, 3].map { |n| n * 2 } # => [2, 4, 6]
"a1b2".filter(~digit?) # => "12"
{ a: 1, b: 2 }.filter { |k, v| v > 1 } # => { :b: 2 }
reduce
Folds the collection from the left. The one operation an Enumerable type must define.
reduce : A -> (A -> T -> A) -> A
Parameters
accA- the initial accumulator
fA -> T -> A- combines the accumulator with each item
Returns: A — the final accumulator
Examples
[1, 2, 3].reduce(1) { |product, n| product * n } # => 6
map
Applies f to each item and collects the results into a list.
The single most useful method here: it describes what each item becomes, and leaves the walking of the collection implied.
map(f)
Parameters
fT -> B- applied to each item
Returns: [B] — the results, in order
Examples
[1, 2, 3].map { |n| n * 2 } # => [2, 4, 6]
["a", "b"].map(~upperCase) # => ["A", "B"]
Extracting one field from a list of records
users.map { |u| u.email }
mapIndexed
Applies f to each item and its 0-based position, and collects the results into a list.
The index is the LAST block parameter: see eachIndexed.
mapIndexed(f)
Parameters
fT -> Integer -> B- applied to each item and its index
Returns: [B] — the results, in order
Examples
["a", "b"].mapIndexed { |s, i| "${i}${s}" } # => ["0a", "1b"]
Building a numbered list
items.mapIndexed { |item, i| "${i + 1}. ${item}" }.join("\n")
filter
Returns the items for which pred answers true.
filter(pred)
Parameters
predT -> Bool- the test applied to each item
Returns: [T] — the matching items, in order
Examples
[1, 2, 3, 4].filter { |n| n.even? } # => [2, 4]
Dropping blank lines
text.lines.filter { |line| !line.trim.empty? }
flatMap
Applies f to each item, expecting a list back, and concatenates the results into one flat list.
Use it when each item expands into zero or more results: map would give you a list of lists.
flatMap(f)
Parameters
fT -> [B]- applied to each item, returning a list
Returns: [B] — all the results, concatenated
Examples
[[1, 2], [3]].flatMap { |xs| xs } # => [1, 2, 3]
["a b", "c"].flatMap { |s| s.split(" ") } # => ["a", "b", "c"]
Every tag used across a list of posts
posts.flatMap { |p| p.tags }
collect
Applies f to each item, expecting an Optional back, and returns the values that were present: unwrapped.
This is filter and map fused into one pass, which is what you want whenever the test and the transformation are the same operation. Parsing is the classic case: an item either yields a value or it does not.
collect(f)
Parameters
fT -> B?- applied to each item, returning an optional
Returns: [B] — the present values, unwrapped, in order
Examples
["1", "x", "3"].collect { |s| s.to(Integer) } # => [1, 3]
Looking several keys up at once, skipping the missing ones
keys.collect { |k| config.get(k) }