struct Pf::Map(K, V)
- Pf::Map(K, V)
- Struct
- Value
- Object
Overview
A thread-safe, persistent, unordered hash map.
Value equality
Being a persistent map, Pf::Map requires path copying. However, path
copying is unnecessary for equal values. If your values can be compared
cheaply, you can make them include Pf::Eq. Then an additional comparison
using #== will take place to figure out whether path copying should occur.
Methods that are marked with supports value equality guarantee to return
exactly self if what they've done resulted in no change in self.
Note that out of the box, #== is called only when your value is of the types
nil, Bool, Char, String, Symbol, or of a primitive number type. And
#same? is called on all reference (Reference) types.
map = Pf::Map[foo: 100, bar: 200]
map.assoc("foo", 100).same?(map) # => true, no change
map.update("foo", 0, &.succ.pred).same?(map) # => true, no change
map2 = Pf::Map[foo: 100, bar: 200]
map.merge(map2).same?(map) # => true, no change
map2.merge(map).same?(map2) # => true, no change
If you want to enable #== for your value, you should make them include
Pf::Eq.
record Info, first_name : String, last_name : String
people = Pf::Map
.assoc(0, Info.new("John", "Doe"))
.assoc(1, Info.new("Barbara", "Doe"))
people.assoc(0, Info.new("John", "Doe")).same?(people) # => false
# ^ Even though the value is the same the map is path-copied.
# v But if we include `Pf::Eq`...
record InfoEq, first_name : String, last_name : String do
include Pf::Eq
end
people = Pf::Map
.assoc(0, InfoEq.new("John", "Doe"))
.assoc(1, InfoEq.new("Barbara", "Doe"))
# ... no path copying is going to be done at the expense of comparing
# the values too.
people.assoc(0, InfoEq.new("John", "Doe")).same?(people) # => true
Since BidiMap is backed by Map, the same applies to it. On the
other hand, elements of a Set are keys so they are always compared
using #== eventually.
Included Modules
Defined in:
permafrost/map.crConstructors
-
.new(enumerable : Enumerable(Tuple(K, V)))
Constructs a
Mapfrom an enumerable of key-value pairs. -
.new : Map(K, V)
Constructs an empty
Map.
Class Method Summary
-
.[](**entries)
A shorthand syntax for creating a
Mapwith string keys. -
.assoc(key : K, value : V) : Map(K, V)
A shorthand for
new.assoc. -
.eqv?(v1 : V, v2 : V) : Bool forall V
Returns
trueif two mapping values v1 and v2 are equal, takingPf::Eqinto account. -
.transaction(& : Commit(K, V) -> ) : Map(K, V)
Shorthand for
new.transaction.
Instance Method Summary
-
#==(other : Map) : Bool
Compares
selfwith other. -
#[](key : K) : V
Returns the value associated with key.
-
#[](key : K, *subkeys)
Traverses nested maps/
Hashes and returns the value. -
#[]?(key : K) : V | Nil
Returns the value associated with key, or nil if key is absent.
-
#[]?(key : K, *subkeys)
Traverses nested maps/
Hashes and returns the value, ornilif the value is absent. -
#assoc(key : K, value : V) : Map(K, V)
Returns a copy of
selfthat contains the association between key and value. -
#clone : Map(K, V)
Returns a new
Mapwhose values are deeply cloned versions of those fromself. -
#compact
Returns a copy of
selfwithoutnilvalues. -
#concat(other : Enumerable(Tuple(K, V))) : Map(K, V)
Returns a new map with associations from
selfand other combined, where other is an enumerable of key-value pairs. -
#dissoc(key : K) : Map(K, V)
Returns a copy of
selfthat is guaranteed not to contain an association with the given key. -
#each(& : Tuple(K, V) -> ) : Nil
Yields each association to the block.
-
#each_key(& : K -> ) : Nil
Yields each key to the block.
-
#each_value(& : V -> ) : Nil
Yields each value to the block.
-
#empty? : Bool
Returns
trueif this map contains no associations. -
#fetch(key : K, & : -> {K, V}) : V | {K, V} forall T
Returns the value mapped to key, or yields if key is absent.
-
#fetch?(key : K) : Tuple(V) | Nil
Returns the value associated with key, or
nilif the value is absent. -
#fmap(& : Tuple(K, V) -> Tuple(K2, V2)) : Map(K2, V2) forall K2, V2
Same as
map, but returnsMapinstead of an array. -
#has_key?(key) : Bool
Alias of
#includes?. -
#hash(hasher)
See
Object#hash(hasher) -
#includes?(key : K) : Bool
Returns
trueif key is present in this map. - #inspect(io)
-
#keys : Array(K)
Returns an array with all keys from this map.
-
#map_key(& : K -> K2) : Map(K2, V) forall K2
Transforms keys: same as
#fmap, but only yields keys from this map. -
#map_value(& : V -> V2) : Map(K, V2) forall V2
Transforms values: same as
#fmap, but only yields values from this map. -
#merge(other : Map(K2, V2)) : Map(K | K2, V | V2) forall K2, V2
Returns a new map with associations from
selfand other combined. -
#merge(other : Map(K2, V2), & : K, V, V2 -> V | V2) : Map(K | K2, V | V2) forall K2, V2
Returns a new map with assocations from
selfand other combined. - #pretty_print(pp) : Nil
-
#reject(& : Tuple(K, V) -> Bool) : Map(K, V)
Returns a copy of
selfwhich includes only associations for which the block is falsey. -
#reject(keys : Enumerable)
Returns a new map which is guaranteed not to include the given keys.
-
#reject(*keys)
Returns a new map which is guaranteed not to include the given keys.
-
#same?(other : Map(K, V)) : Bool
Returns
trueifselfand other refer to the same map in memory. -
#select(& : Tuple(K, V) -> Bool) : Map(K, V)
Returns a copy of
selfwhich includes only associations for which the block is truthy. -
#select(keys : Enumerable)
Returns a new map which includes only associations with the given keys.
-
#select(*keys)
Returns a new map which includes only associations with the given keys.
-
#size : Int32
Returns the number of associations in this map.
- #to_s(io)
-
#transaction(& : Commit(K, V) -> ) : Map(K, V)
Yields a
Commitobject which allows you to mutate a copy ofself. -
#update(key : K, default : V, & : V -> V)
Returns an updated copy of
self. -
#update(key : K, & : V -> V) : Map(K, V)
Returns an updated copy of
self. -
#values : Array(V)
Returns an array with all values from this map.
Instance methods inherited from module Enumerable({K, V})
to_pf_bidi
to_pf_bidi,
to_pf_map(& : {K, V} -> Tuple(K, V)) : Pf::Map(K, V) forall K, Vto_pf_map to_pf_map, to_pf_set : Pf::Set({K, V}) to_pf_set
Constructor Detail
Constructs a Map from an enumerable of key-value pairs.
Class Method Detail
A shorthand syntax for creating a Map with string keys. The type
of the map's values is the union of the types of values in entries.
map = Pf::Map[name: "John Doe", age: 25]
map["name"] # => "John Doe"
map["age"] # => 25
typeof(map) # => Pf::Map(String, String | Int32)
Returns true if two mapping values v1 and v2 are equal, taking
Pf::Eq into account.
Shorthand for new.transaction.
Instance Method Detail
Compares self with other. Returns true if all associations are
the same (values are compared using #==).
Returns the value associated with key. Raises KeyError if key
is absent.
map = Pf::Map[foo: 10]
map["foo"] # => 10
map["bar"] # raises KeyError
Traverses nested maps/Hashes and returns the value. Raises
KeyError if there is no value.
map = Pf::Map[foo: Pf::Map[bar: {100 => Pf::Map[baz: "Yay!"]}]]
map["foo", "bar", 100, "baz"] # => "Yay!"
map["foo", "bar", 200] # raises KeyError
Returns the value associated with key, or nil if key is absent.
map = Pf::Map[foo: 10, bar: 20]
map["foo"]? # => 10
map["bar"]? # => 20
map["baz"]? # => nil
Traverses nested maps/Hashes and returns the value, or nil if
the value is absent.
map = Pf::Map[foo: Pf::Map[bar: {100 => Pf::Map[baz: "Yay!"]}]]
map["foo", "bar", 100, "baz"]? # => "Yay!"
map["foo", "bar", 200]? # => nil
Returns a copy of self that contains the association between key and value.
Supports value equality.
map = Pf::Map(String, Int32).new
branch1 = map.assoc("foo", 100)
branch2 = map.assoc("foo", 200)
map = map.assoc("bar", 300)
map["foo"]? # => nil
map["bar"]? # => 300
branch1["foo"]? # => 100
branch1["bar"]? # => nil
branch2["foo"]? # => 200
branch2["bar"]? # => nil
Returns a new Map whose values are deeply cloned versions of
those from self. That is, returns a deep copy of self.
Keys are not cloned (if you need to clone keys then the last thing to help you is a persistent immutable map!).
map = Pf::Map[foo: [1, 2, 3], bar: [4, 5, 6]]
map2 = map.clone
map["foo"][0] = 100
map # => Pf::Map{"foo" => [100, 2, 3], "bar" => [4, 5, 6]}
map2 # => Pf::Map{"foo" => [1, 2, 3], "bar" => [4, 5, 6]}
Returns a copy of self without nil values.
map = Pf::Map[foo: nil, bar: 123]
map.compact # => Pf::Map{"bar" => 123}
typeof(map) # => Pf::Map(String, Int32?)
typeof(map.compact) # => Pf::Map(String, Int32)
Returns a new map with associations from self and other combined, where
other is an enumerable of key-value pairs.
Supports value equality.
map = Pf::Map[foo: 100, bar: 200, baz: 300]
map.concat([{"x", 123}, {"y", 456}]) # => Pf::Map[foo: 100, bar: 200, baz: 300, x: 123, y: 456]
Returns a copy of self that is guaranteed not to contain an association
with the given key.
map = Pf::Map[foo: 100, bar: 200]
branch1 = map.dissoc("foo")
branch2 = map.dissoc("bar")
map["foo"]? # => 100
map["bar"]? # => 200
branch1["foo"]? # => nil
branch1["bar"]? # => 200
branch2["foo"]? # => 100
branch2["bar"]? # => nil
Returns the value mapped to key, or yields if key is absent. This method mainly exists to circumvent nil as in value vs. nil as in absence issue.
Returns the value associated with key, or nil if the value is absent.
The value is wrapped in a tuple to differentiate between nil as value
and nil as absence.
map = Pf::Map[name: "John Doe", job: nil]
map.fetch?("job") # => {nil}
map.fetch?("name") # => {"John Doe"}
map.fetch?("age") # => nil
if name_t = map.fetch?("name")
name, *_ = name_t
name # => "John Doe"
end
Same as map, but returns Map instead of an array.
If the block returns more than one value for the same key, the last yielded value is preferred.
Supports value equality if K == K2 and V == V2.
map = Pf::Map[foo: "John Doe", bar: "Samantha Doe"]
map.fmap { |k, v| {k.upcase, v.upcase} } # => Pf::Map{"FOO" => "JOHN DOE", "BAR" => "SAMANTHA DOE"}
Returns true if key is present in this map.
map = Pf::Map[foo: 100, bar: 200]
"foo".in?(map) # => true
"bar".in?(map) # => true
"baz".in?(map) # => false
Returns an array with all keys from this map. There is no guaranteed order of keys.
map = Pf::Map[foo: 10, bar: 20]
map.keys # => ["foo", "bar"]
Transforms keys: same as #fmap, but only yields keys from this map.
Supports value equality if K == K2.
map = Pf::Map[foo: "John Doe", bar: "Samantha Doe"]
map.map_key(&.upcase) # => Pf::Map{"FOO" => "John Doe", "BAR" => "Samantha Doe"}
Transforms values: same as #fmap, but only yields values from
this map.
Supports value equality if V == V2.
map = Pf::Map[foo: "John Doe", bar: "Samantha Doe"]
map.map_value(&.upcase) # => Pf::Map{"foo" => "JOHN DOE", "bar" => "SAMANTHA DOE"}
Returns a new map with associations from self and other combined.
If some key is common both to self and other, other's value
is preferred.
Supports value equality if K == K2 and V == V2.
a = Pf::Map[foo: 100, bar: 200]
b = Pf::Map[foo: "hello", baz: true, boo: 500]
map = a.merge(b)
map # => Pf::Map{"foo" => "hello", "bar" => 200, "baz" => true, "boo" => 500}
typeof(map) # => Pf::Map(String, String | Int32 | Bool)
Returns a new map with assocations from self and other combined.
If some key is common both to self and other, that key is
yielded to the block together with the two values. The return
value of the block is used as the final value.
a = Pf::Map[foo: 100, bar: 200, baz: 300]
b = Pf::Map[foo: 200, bar: 300.8, boo: 1000.5]
map = a.merge(b) { |k, v1, v2| v1 + v2 }
map # => Pf::Map{"foo" => 300, "bar" => 500.8, "baz" => 300, "boo" => 1000.5}
typeof(map) # => Pf::Map(String, Int32 | Float64)
Returns a copy of self which includes only associations for which
the block is falsey.
Supports value equality.
map = Pf::Map[foo: 2, bar: 3, baz: 4, boo: 5]
map.reject { |_, v| v.even? } # => Pf::Map{"bar" => 3, "boo" => 5}
Returns a new map which is guaranteed not to include the given keys.
map = Pf::Map[foo: 2, bar: 3, baz: 4, boo: 5]
map.reject({"foo", "boo"}) # => Pf::Map{"bar" => 3, "baz" => 4}
map.reject("foo", "boo") # => Pf::Map{"bar" => 3, "baz" => 4}
Returns a new map which is guaranteed not to include the given keys.
map = Pf::Map[foo: 2, bar: 3, baz: 4, boo: 5]
map.reject({"foo", "boo"}) # => Pf::Map{"bar" => 3, "baz" => 4}
map.reject("foo", "boo") # => Pf::Map{"bar" => 3, "baz" => 4}
Returns true if self and other refer to the same map in memory.
Due to the way Map is implemented, this method can be used as
a cheap way to detect changes.
map1 = Pf::Map[foo: 123, bar: 456]
map2 = map1.assoc("foo", 123)
map1.same?(map2) # => true
Returns a copy of self which includes only associations for which
the block is truthy.
Supports value equality.
map = Pf::Map[foo: 2, bar: 3, baz: 4, boo: 5]
map.select { |_, v| v.even? } # => Pf::Map{"foo" => 2, "baz" => 4}
Returns a new map which includes only associations with the given keys.
map = Pf::Map[foo: 2, bar: 3, baz: 4, boo: 5]
map.select({"foo", "boo"}) # => Pf::Map{"foo" => 2, "boo" => 5}
map.select("foo", "boo") # => Pf::Map{"foo" => 2, "boo" => 5}
Returns a new map which includes only associations with the given keys.
map = Pf::Map[foo: 2, bar: 3, baz: 4, boo: 5]
map.select({"foo", "boo"}) # => Pf::Map{"foo" => 2, "boo" => 5}
map.select("foo", "boo") # => Pf::Map{"foo" => 2, "boo" => 5}
Yields a Commit object which allows you to mutate a copy of self.
-
The commit object is marked as resolved after the block. You should not retain it. If you do, all operations on the object (including readonly ones) will raise
ResolvedError. -
If you pass the commit object to another fiber in the block, e.g. via a channel, and fiber yield immediately after that, the commit obviously would not be marked as resolved as the resolution code would not have been reached yet. However, if you then attempt to call mutation methods on the commit, another error,
ReadonlyError, will be raised. In other words, the yielded commit object is readonly for any other fiber except for the fiber that it was originally yielded to.
Returns self if the transaction did not touch the map. If the map was
changed but then the changes were reverted this method will return a new map.
map1 = Pf::Map(String, Int32).new
map2 = map1.transaction do |commit|
commit.assoc("John Doe", 12)
commit.assoc("Susan Doe", 34)
commit.dissoc("John Doe")
if "John Doe".in?(commit)
commit.assoc("Mark Doe", 21)
else
commit.assoc("John Doe", 456)
commit.assoc("Susan Doe", commit["Susan Doe"] + 1)
end
end
map1 # => Pf::Map[]
map2 # => Pf::Map["John Doe" => 456, "Susan Doe" => 35]
Returns an updated copy of self.
-
If there is no association for key, the copy contains an association between key and default.
-
If there is an association for key, its value is yielded to the block and the return value of the block is used as the next value of key.
Supports value equality.
map = Pf::Map[foo: 100, bar: 200]
map.update("foo", 0, &.succ) # => Pf::Map{"foo" => 101, "bar" => 200}
map.update("baz", 0, &.succ) # => Pf::Map{"foo" => 100, "bar" => 200, "baz" => 0}
Returns an updated copy of self.
- If there is no association for key, returns
self. - If there is an association for key, its value is yielded to the block and the return value of the block is used as the next value of key.
Supports value equality.
map = Pf::Map[foo: 100, bar: 200]
map.update("foo", &.succ) # => Pf::Map{"foo" => 101, "bar" => 200}
map.update("baz", &.succ) # => Pf::Map{"foo" => 100, "bar" => 200}
Returns an array with all values from this map. There is no guaranteed order of values.
map = Pf::Map[foo: 10, bar: 20]
map.values # => [10, 20]