View Source Lens2.Lenses.BiMap (Lens 2 v0.1.0)

Lens makers for the BiMap bidirectional map package.

A BiMap allows a "reverse lookup": using a value to find the corresponding key.

iex>  bimap = BiMap.new(%{1 => "1111", 2 => "2222"})
iex>  BiMap.get(bimap, 1)
"1111"
iex>  BiMap.get_key(bimap, "1111")
1

A BiMap differs importantly from a map in that it requires values, not just keys, to be unique. This can cause some confusion as BiMap's put operation can cause existing bindings to disappear.

 iex>  bimap = BiMap.new(a: 5, b: 6)
 iex>  BiMap.put(bimap, :b, 5)
 BiMap.new(b: 5)   # where did `:a` go?

I've found the problem gets worse with lenses (because you may be putting multiple places in a single call, and the place you want to change may be nested below the BiMap, making it harder to notice the constraint is relevant.) You might prefer BiMultiMap (which comes with BiMap).

Note that BiMap does not implement Access. However, lenses created with these functions do, so they can be used with get_in/2 and friends.

This module follows the convention of using names like key to mean using a key to obtain a value. Names like to_key go in the reverse direction: from value to key. Using value(1) to refer to the key :a would be just too weird.

Summary

Functions

Return a lens that points at all keys of a BiMap.

Return a lens that points at all values of a BiMap.

Return a lens that points at the value of a single BiMap key.

Like key/1 and key?/1 except that the lens will raise an error for a missing key.

Return a lens that points at the value of a single BiMap key, ignoring missing keys.

Like key/1 but takes a list of keys.

Like key!/1 but takes a list of keys.

Like key?/1 but takes a list of keys.

Return a lens that points at the key associated with a given value. Raises an error if there is no such value.

Return a lens that points at the key associated with a given value, provided there is any such value.

Like to_key!/1 but takes a list of values.

Like to_key?/1 but takes a list of values.

Functions

@spec all_keys() :: Lens2.lens()

Return a lens that points at all keys of a BiMap.

This is useful for descending into complex keys. Consider a spatial BiMap that connects {x, y} tuples to some values representing physical objects at that position.

iex>  bimap = BiMap.new(%{{0, 0} => "some data", {1, 1} => "other data"})
iex>  Deeply.update(bimap, Lens.BiMap.all_keys,
...>                       fn {x, y} -> {x + 1, y + 1} end)
iex>  BiMap.new(%{{1, 1} => "some data", {2, 2} => "other data"})

Note that all the updates are done before the result BiMap is formed. You needn't fear that updating the {0, 0} tuple will wipe out the existing {1, 1} tuple.

@spec all_values() :: Lens2.lens()

Return a lens that points at all values of a BiMap.

iex>  bimap = BiMap.new(%{1 => "1111", 2 => "2222"})
iex>  Deeply.get_all(bimap, Lens.BiMap.all_values) |> Enum.sort
["1111", "2222"]

Note that using put with all_values is profoundly useless, as only one key/value pair can be retained – and you can't even predict which one it will be!

iex>  bimap = BiMap.new(a: 1, b: 2, c: 3, d: 4, e: 5)
iex>  updated = Deeply.put(bimap, Lens.BiMap.all_values, :NEW)
iex>  assert [:NEW] == BiMap.values(updated)
...>
iex>  [key] = BiMap.keys(updated)
iex>  assert key in [:a, :b, :c, :d, :e]
...>  # On my machine, today, it turns out to be `:e`.
@spec key(any()) :: Lens2.lens()

Return a lens that points at the value of a single BiMap key.

As with Lens2.Lenses.Keyed.key/1, a missing key is represented with nil:

iex>  bimap = BiMap.new(a: 1, b: 2)
...>  lens = [:a, :MISSING] |> Enum.map(&Lens.BiMap.key/1) |> Lens.multiple
iex>  Deeply.get_all(bimap, lens) |> Enum.sort
[1, nil]

This lens can introduce a missing key-value pair, unlike key?/1.

iex> Deeply.put(BiMap.new, Lens.BiMap.key(:missing), :NEW)
BiMap.new(%{missing: :NEW})
@spec key!(any()) :: Lens2.lens()

Like key/1 and key?/1 except that the lens will raise an error for a missing key.

This works the same as Lens2.Lenses.Keyed.key!/1.

iex>  bimap = BiMap.new(a: 1)
iex>  Deeply.put(bimap, Lens.BiMap.key!(:a), :NEW)
BiMap.new(a: :NEW)
iex>  Deeply.put(bimap, Lens.BiMap.key!(:missing), :NEW)
** (ArgumentError) key :missing not found in: BiMap.new([a: 1])
@spec key?(any()) :: Lens2.lens()

Return a lens that points at the value of a single BiMap key, ignoring missing keys.

This works the same as Lens2.Lenses.Keyed.key?/1.

iex>  bimap = BiMap.new(a: 1, b: 2)
...>  lens = [:a, :MISSING] |> Enum.map(&Lens.BiMap.key?/1) |> Lens.multiple
iex>  Deeply.get_all(bimap, lens)
[1]

Unlike key/1, the returned lens cannot be used to create a missing key:

iex> Deeply.put(BiMap.new, Lens.BiMap.key?(:missing), :NEW)
BiMap.new
@spec keys([any()]) :: Lens2.lens()

Like key/1 but takes a list of keys.

The value of a missing key is treated as nil.

iex>  bimap = BiMap.new(a: 2)
iex>  lens = Lens.BiMap.keys([:a, :missing])
iex>  updater = fn
...>    nil -> "newly added"
...>    x   -> x * 1111
...>  end
iex>  Deeply.update(bimap, lens, updater)
BiMap.new(a: 2222, missing: "newly added")
@spec keys!([any()]) :: Lens2.lens()

Like key!/1 but takes a list of keys.

Any missing key raises an error.

iex>  bimap = BiMap.new(a: 2)
iex>  lens = Lens.BiMap.keys!([:a, :missing])
iex>  Deeply.get_only(bimap, lens)
** (ArgumentError) key :missing not found in: BiMap.new([a: 2])
@spec keys?([any()]) :: Lens2.lens()

Like key?/1 but takes a list of keys.

Missing keys are ignored.

iex>  bimap = BiMap.new(a: 2)
iex>  lens = Lens.BiMap.keys?([:a, :missing])
iex>  Deeply.update(bimap, lens, & &1*1111)
BiMap.new(a: 2222)
iex>  Deeply.get_only(bimap, lens)
2
@spec to_key!(any()) :: Lens2.lens()

Return a lens that points at the key associated with a given value. Raises an error if there is no such value.

iex>  bimap = BiMap.new(a: 1)
iex>  Deeply.get_all(bimap, Lens.BiMap.to_key!(1))
[:a]
iex>  Deeply.get_all(bimap, Lens.BiMap.to_key!(11111))
** (ArgumentError) value 11111 not found in: BiMap.new([a: 1])
@spec to_key?(any()) :: Lens2.lens()

Return a lens that points at the key associated with a given value, provided there is any such value.

iex>  bimap = BiMap.new(a: 1)
iex>  Deeply.get_all(bimap, Lens.BiMap.to_key?(1))
[:a]
iex>  Deeply.get_all(bimap, Lens.BiMap.to_key?(11111))
[]

You can create a new key-value pair with key/1. Lens2.Deeply.put/3 will not work with this lens:

iex> Deeply.put(BiMap.new, Lens.BiMap.to_key?("value"), :new_key)
BiMap.new
@spec to_keys!([any()]) :: Lens2.lens()

Like to_key!/1 but takes a list of values.

Any missing value raises an error.

iex>  bimap = BiMap.new(a: 2)
iex>  lens = Lens.BiMap.to_keys!([2, "missing value"])
iex>  Deeply.get_all(bimap, lens)
** (ArgumentError) value "missing value" not found in: BiMap.new([a: 2])
@spec to_keys?([any()]) :: Lens2.lens()

Like to_key?/1 but takes a list of values.

Missing keys are ignored.

iex>  bimap = BiMap.new(%{1 => "value"})
iex>  lens = Lens.BiMap.to_keys?(["value", "some missing value"])
iex>  Deeply.update(bimap, lens, & &1*1111)
BiMap.new(%{1111 => "value"})
iex>  Deeply.get_only(bimap, lens)
1