struct

Pf::Map(K, V)

Inherits Enumerable / Pf::Kit / Struct / Value / Object

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.

Constructors

new(enumerable : Enumerable(Tuple(K, V)))

Constructs a Map from an enumerable of key-value pairs.

Source
new

Constructs an empty Map.

Source

Class methods

[]

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)
Source
assoc(key : K, value : V) : Map(K, V)

A shorthand for new.assoc.

Source
eqv?(v1 : V, v2 : V) : Bool forall V

Returns true if two mapping values v1 and v2 are equal, taking Pf::Eq into account.

Source
transaction

Shorthand for new.transaction.

Source

Instance methods

==(other : Map) : Bool

Compares self with other. Returns true if all associations are the same (values are compared using ==).

Source
[](key : K) : V

Returns the value associated with key. Raises KeyError if key is absent.

map = Pf::Map[foo: 10]
map["foo"] # => 10
map["bar"] # raises KeyError
Source
[](key : K, *subkeys)

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
Source
[]?(key : K) : V | Nil

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
Source
[]?(key : K, *subkeys)

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
Source
assoc(key : K, value : V) : Map(K, V)

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
Source
clone

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]}
Source
compact

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)
Source
concat(other : Enumerable(Tuple(K, V))) : Map(K, V)

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]
Source
dissoc(key : K) : Map(K, V)

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
Source
each

Yields each association to the block.

Source
each_key

Yields each key to the block.

Source
each_value

Yields each value to the block.

Source
empty?

Returns true if this map contains no associations.

Source
fetch(key : K, & : -> {K, V}) : V | {K, V} forall T

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.

Source
fetch?(key : K) : Tuple(V) | Nil

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
Source
fmap

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"}
Source
has_key?(key) : Bool

Alias of includes?.

Source
hash(hasher)

See Object#hash(hasher)

Source
includes?(key : K) : Bool

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
Source
inspect(io)
Source
keys

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"]
Source
map_key

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"}
Source
map_value

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"}
Source
merge(other : Map(K2, V2)) : Map(K | K2, V | V2) forall K2, V2

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)
Source
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 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)
Source
nth?(n : Int32) : Tuple(K, V) | Nil

Returns the n-th association in this map. Their order is the same as in each and is determined by the hash function. n can range from 0 to size - 1 (otherwise returns nil).

Source
pretty_print(pp) : Nil
Source
reject

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}
Source
reject(keys : Enumerable)

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}
Source
reject(*keys)

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}
Source
same?(other : Map(K, V)) : Bool

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
Source
select

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}
Source
select(keys : Enumerable)

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}
Source
select(*keys)

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}
Source
size

Returns the number of associations in this map.

Source
to_s(io)
Source
transaction

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]
Source
update(key : K, default : V, & : V -> V)

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}
Source
update(key : K, & : V -> V) : Map(K, V)

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}
Source
values

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]
Source

Nested types