Map
An immutable key-value store, written {key: value}.
Keys are compared by structural equality and may be of any type; atom keys get the shorthand {name: "Ada"}, string keys are written out in full as {"name": "Ada"}. Every method answers with a new map: the ! forms (put!, delete!) build a new map and rebind the receiver variable, they do not modify anything in place.
Entries come back in canonical key order, not insertion order, so keys, values, entries and any traversal are stable and comparable across equal maps.
let config = { host: "localhost", port: 8080 }
config.get(:host).or("0.0.0.0") # => "localhost"
config.get(:user).or("anonymous") # => "anonymous"
config.put(:port, 9090) # => { :host: "localhost", :port: 9090 }
A map is Enumerable and Foldable, and the traversal blocks take the key and value as two parameters:
config.each { |k, v| IO.printLine("${k} = ${v}") }
config.filter { |k, v| k != :port } # => { :host: "localhost" }
type Map<K, V>
Declared for the same reason list.kex declares type List<X> = [X]: it gives the name Map a source declaration, so it resolves as a type through the collected interfaces rather than needing to be known to the compiler.
Implements Enumerable, Foldable, Monoid, Blankable, Truthyable.
Methods
reduce Enumerable Foldable
reduce(acc: A, g: (A -> (K, V) -> A)) -> A
Folds over the map's (key, value) pairs in canonical key order.
This is Map's Enumerable primitive: map, filter, find, any? and the rest are built on it. The block receives the accumulator and one pair; destructure the pair to name its halves.
Parameters
acc- the initial accumulator
g- combines the accumulator with each pair
Returns the final accumulator
Examples
Summing the values
{ a: 1, b: 2 }.reduce(0) do |acc, pair|
let (key, value) = pair
acc + value
end
# => 3
Rendering the map as a query string
{ a: 1, b: 2 }.entries.map { |k, v| "${k}=${v}" }.join("&")
identity Monoid
The empty map: the identity element of the Monoid instance, so m.combine({}) is m.
Returns Map<K, V> — the empty map
Examples
Map.identity # => {}
combine Monoid
combine(other: This) -> This
Combines two maps by merging them, with other's values winning on a key conflict. The Monoid operation, and the same thing merge does.
Parameters
other- the map to merge in
Returns the combined map
Examples
{ a: 1 }.combine({ b: 2 }) # => { :a: 1, :b: 2 }
Folding a list of maps into one
[{ a: 1 }, { b: 2 }, { a: 9 }].reduce({}) { |acc, m| acc.combine(m) }
# => { :a: 9, :b: 2 }
get
get(key: K) -> V?
get(key: K, default: V) -> V
Returns the value stored under key, or None when the key is absent.
Missing keys are an ordinary answer rather than a failure, so a lookup on data you did not produce is safe by default. Use the two-argument form below when you have a sensible fallback.
Parameters
key- the key to look up
Returns the value, or None
Examples
let user = { name: "Alice", age: 32 }
user.get(:name) # => Just("Alice")
user.get(:missing) # => None
Chaining through a nested map
settings.get(:server).flatMap { |s| s.get(:port) }.or(8080)
put
put(k: K, v: V) -> Map<K, V>
Returns a new map with key mapped to value, replacing any previous entry for that key.
The receiver is untouched. Use put! when you want the variable holding the map to be rebound to the result.
Parameters
k- the key to set
v- the value to store
Returns a new map including the entry
Examples
{}.put(:x, 1) # => { :x: 1 }
{ x: 1 }.put(:x, 2) # => { :x: 2 }
Rebinding with the +!+ form
var totals = {}
totals.put!(:visits, 1)
totals # => { :visits: 1 }
delete
delete(key: K) -> Map<K, V>
Returns a new map without key. A key that is not present is not an error: the map comes back unchanged.
Use delete! to rebind the receiver variable.
Parameters
key- the key to remove
Returns a new map without that entry
Examples
{ a: 1, b: 2 }.delete(:a) # => { :b: 2 }
{ a: 1 }.delete(:z) # => { :a: 1 }
Stripping a secret before logging
IO.printLine(params.delete(:password))
has?
has?(key: K) -> Bool
Returns true when key has an entry in the map.
Distinguishes a missing key from one whose value is itself empty, which a get with a default cannot.
Parameters
key- the key to test
Returns true when the key is present
Examples
{ a: 1 }.has?(:a) # => true
{ a: 1 }.has?(:z) # => false
Checking a required setting
if !config.has?(:host)
IO.printError("host is required")
end
empty?
empty? : Bool
Returns true when the map has no entries.
Returns true for the empty map
Examples
{}.empty? # => true
{ a: 1 }.empty? # => false
count Foldable
count : Integer
Returns the number of entries.
Returns the entry count
Examples
{ a: 1, b: 2 }.count # => 2
{}.count # => 0
count(pred: (K -> V -> Bool)) -> Integer
Returns the number of entries satisfying pred.
Parameters
pred- the test applied to each entry
Returns how many entries matched
Examples
{ a: 1, b: 2 }.count { |k, v| v > 1 } # => 1
How many settings are still at their default
config.count { |k, v| v == defaults.get(k, v) }
keys
keys : [K]
Returns the map's keys as a list, in canonical key order.
Returns the keys
Examples
{ b: 1, a: 2 }.keys # => [:a, :b]
Reporting unrecognised options
params.keys.reject { |k| known.contains?(k) }
values
values : [V]
Returns the map's values as a list, ordered by their keys.
Returns the values
Examples
{ a: 1, b: 2 }.values # => [1, 2]
{ a: 1, b: 2 }.values.sum # => 3
entries
entries : [(K, V)]
Returns the map's entries as a list of (key, value) tuples, in canonical key order.
This is the bridge to the List methods a map does not have of its own, and the form the two-parameter blocks elsewhere are splatting from.
Returns the entries
Examples
{ a: 1, b: 2 }.entries # => [(:a, 1), (:b, 2)]
Sorting entries by value
scores.entries.sort { |x, y| x.items.last.or(0) > y.items.last.or(0) }
each Foldable
each(f: (K -> V -> Void)) -> Void
Calls f with each key and value, for its side effects.
Parameters
f- called once per entry
Examples
scores.each { |k, v| IO.printLine("${k}: ${v}") }
map Enumerable
map(f: (K -> V -> R)) -> [R]
Applies f to each key and value and collects the results into a LIST.
Note the return type: map comes from Enumerable, whose contract is to produce a list, because f may return anything at all. Use mapValues or mapKeys when you want a map back.
Parameters
f- applied to each entry
Returns the results, in canonical key order
Examples
{ "a": 1, "b": 2 }.map { |k, v| "${k}=${v}" } # => ["a=1", "b=2"]
Building a header block
headers.map { |name, value| "${name}: ${value}" }.join("\n")
mapValues
mapValues(f: (V -> W)) -> Map<K, W>
Returns a new map with every value replaced by f(value). The keys are left alone.
Parameters
f- applied to each value
Returns a map with the same keys and transformed values
Examples
{ a: 1, b: 2 }.mapValues { |v| v * 10 } # => { :a: 10, :b: 20 }
Normalising values read as text
raw.mapValues { |s| s.trim.lowerCase }
mapKeys
mapKeys(f: (K -> J)) -> Map<J, V>
Returns a new map with every key replaced by f(key). The values are left alone.
If f maps two keys onto the same result, one entry wins: the map cannot hold both.
Parameters
f- applied to each key
Returns a map with transformed keys
Examples
{ "a": 1, "b": 2 }.mapKeys { |k| k.upperCase } # => { A: 1, B: 2 }
Making header lookups case-insensitive
headers.mapKeys { |name| name.lowerCase }
filter Enumerable
filter(pred: (K -> V -> Bool)) -> Map<K, V>
Returns a new map with only the entries for which pred answers true.
Map overrides the map-returning HOFs (Enumerable's default returns a list).
Parameters
pred- the test applied to each entry
Returns the matching entries
Examples
{ a: 1, b: 2, c: 3 }.filter { |k, v| v > 1 } # => { :b: 2, :c: 3 }
Keeping only the options that were actually set
options.filter { |name, value| !value.blank? }
reject
reject(pred: (K -> V -> Bool)) -> Map<K, V>
Returns a new map with the entries for which pred answers true removed. The complement of filter.
Parameters
pred- the test applied to each entry
Returns the entries that failed the predicate
Examples
{ a: 1, b: 2, c: 3 }.reject { |k, v| v > 1 } # => { :a: 1 }
Dropping internal keys before serialising
record.reject { |name, _| name.startsWith?("_") }
merge
merge(other: Map<K, V>) -> Map<K, V>
Returns a new map holding the entries of both. When a key appears in both, other's value wins.
The right-biased rule is what makes this the natural way to apply overrides on top of defaults.
Parameters
other- the map whose values take precedence
Returns the combined map
Examples
{ a: 1, b: 2 }.merge({ b: 99, c: 3 }) # => { :a: 1, :b: 99, :c: 3 }
Layering user settings over defaults
defaults.merge(userConfig)
any? Foldable
any?(pred: (K -> V -> Bool)) -> Bool
Returns true when at least one entry satisfies pred. Stops at the first match.
Parameters
pred- the test applied to each entry
Returns true when any entry matches
Examples
{ a: 1, b: 2 }.any? { |k, v| v > 1 } # => true
{ a: 1, b: 2 }.any? { |k, v| v > 9 } # => false
all? Foldable
all?(pred: (K -> V -> Bool)) -> Bool
Returns true when every entry satisfies pred. The empty map answers true.
Parameters
pred- the test applied to each entry
Returns true when all entries match
Examples
{ a: 1, b: 2 }.all? { |k, v| v > 0 } # => true
{ a: 1, b: 2 }.all? { |k, v| v > 1 } # => false
Validating a form
fields.all? { |name, value| !value.blank? }
find Foldable
find(pred: (K -> V -> Bool)) -> (K, V)?
Returns the first entry satisfying pred as a (key, value) tuple, or None when nothing matches.
"First" means first in canonical key order.
Parameters
pred- the test applied to each entry
Returns the matching entry, or None
Examples
{ a: 1, b: 2 }.find { |k, v| v > 1 } # => Just((:b, 2))
{ a: 1, b: 2 }.find { |k, v| v > 9 } # => None
Locating a value without knowing its key
users.find { |id, user| user.email == target }
From Enumerable
mapIndexed- Applies
fto each item and its 0-based position, and collects the results into a list. flatMap- Applies
fto each item, expecting a list back, and concatenates the results into one flat list. collect- Applies
fto each item, expecting anOptionalback, and returns the values that were present: unwrapped.
From Foldable
eachIndexed- Calls
fwith each item and its 0-based position, for its side effects.
From Monoid
repeat- Combines this value with itself
ntimes.