One9.Ms (omultiset v0.3.0)
Operations on simple non-struct multiplicity maps ("multisets", t/1).
"Lax" multisets (t_lax/1) may include some entries for absent elements, and are, by
default, handled correctly by all methods in this module.
iex> One9.Ms.counts([1, 2, 1, 3]) # like Python's "collections.Counter(...)"
%{1 => 2, 2 => 1, 3 => 1} # 1 occurs twice, 2 occurs once, and 3 occurs once.
iex> One9.Ms.to_list(%{1 => 2, 2 => 1, 3 => 1}) |> Enum.sort()
[1, 1, 2, 3] # the original list is got back, order notwithstanding
iex> One9.Ms.counts(nil: 5) # WARNING: GENERALLY NOT WHAT YOU WANT...
...> |> One9.Ms.to_list()
[{nil, 5}] # the tuple {nil, 5} occurs once.
iex> One9.Ms.from_counts(nil: 5) # ...USE THIS INSTEAD
...> |> One9.Ms.to_list()
[nil, nil, nil, nil, nil] # the atom nil occurs ten times.
iex> One9.Ms.member?(%{"damn" => 0}, "damn")
false # "damn" occurs zero times, so it is considered not to be a member
Summary
Functions
A more efficient alternative to Enum.to_list(ms) |> Enum.at(index).
Determine the multiplicity of an element in a multiset.
Create a well-formed multiset from any (finite) Enumerable of values.
Delete copies of an element from a multiset.
Return the first multiset, less the elements in the second.
Return the first multiset, less the elements in the second.
Determine whether the multiset is empty.
Determine whether two multisets are equal.
Construct a well-formed multiset from any enumerable of multiplicities (t0/0).
Return the intersection of two multisets.
Determine whether a value is present at all in a multiset.
Add additional copies (by default) of the element into the multiset.
Determine the cardinality of a multiset.
Determine whether the first multiset is a (non-strict) subset of the second.
Convert a multiset into a List of unique elements.
Return the cardinality of the support of a multiset.
Convert a multiset into a complete List of elements (including repeats).
Return the union of two multisets.
Types
@type t0(value) :: Enumerable.t({value, non_neg_integer()})
@type t(value) :: %{optional(value) => pos_integer()}
@type t_lax(value) :: %{optional(value) => non_neg_integer()}
Functions
@spec at(t0(e), non_neg_integer()) :: e when e: term()
A more efficient alternative to Enum.to_list(ms) |> Enum.at(index).
iex> %{41 => 100, 42 => 10**100, 43 => 100} # an extremely large multiset
...> |> One9.Ms.at(10**99) # pull some element out of the middle of it
42
@spec count_element(t_lax(), term()) :: non_neg_integer()
Determine the multiplicity of an element in a multiset.
iex> ms = One9.Ms.counts(["duck", "duck", "goose", "duck"])
%{"duck" => 3, "goose" => 1}
iex> ms |> One9.Ms.count_element("duck")
3
iex> ms |> One9.Ms.count_element("buffalo")
0See also size/1.
@spec counts(Enumerable.t(e)) :: t(e) when e: term()
Create a well-formed multiset from any (finite) Enumerable of values.
See also from_counts/1.
@spec delete(t(e), term(), non_neg_integer() | :all) :: t(e) when e: term()
@spec delete(t_lax(e), term(), non_neg_integer() | :all) :: t_lax(e) when e: term()
Delete copies of an element from a multiset.
Returns a well-formed multiset whenever input is well-formed.
@spec difference(t(e), t_lax()) :: t(e) when e: term()
@spec difference(t_lax(e), t_lax()) :: t_lax(e) when e: term()
Return the first multiset, less the elements in the second.
Calculations are done soft aka "clamping".
Returns a well-formed multiset whenever the first argument is well-formed.
iex> %{"dog" => 1, "cat" => 10}
...> |> One9.Ms.difference(%{"dog" => 3, "cat" => 3})
%{"cat" => 7}See also difference!/2.
@spec difference!(t(e), t_lax(e)) :: t(e) when e: term()
@spec difference!(t_lax(e), t_lax(e)) :: t_lax(e) when e: term()
Return the first multiset, less the elements in the second.
Raises KeyError if the second element is not a subset of the first.
Returns a well-formed multiset whenever the first argument is well-formed.
iex> %{"dog" => 1, "cat" => 10}
...> |> One9.Ms.difference!(%{"dog" => 3, "cat" => 3})
** (KeyError) key {"dog", 3} not found in: %{"cat" => 10, "dog" => 1}See also difference/2.
Determine whether the multiset is empty.
Examples
iex> One9.Ms.empty?(%{})
true
iex> One9.Ms.empty?(%{"pony" => 1})
false
iex> One9.Ms.empty?(%{"lie" => 0})
trueSee also size/1.
Determine whether two multisets are equal.
Examples
iex> One9.Ms.equals?(%{"dog" => 999}, %{"cat" => 1})
false
iex> One9.Ms.equals?(%{"dollars" => 0}, %{"cents" => 0})
trueSee also size/1.
Construct a well-formed multiset from any enumerable of multiplicities (t0/0).
Duplicate entries are combined additively.
Examples
iex> One9.Ms.from_counts(true: 3, false: 99, true: 1)
%{true: 4, false: 99}See also counts/1.
Return the intersection of two multisets.
Either/both operands may be non-well-formed. Result will always be well-formed.
iex> One9.Ms.intersection(%{a: 1, b: 2, c: 3}, %{b: 1, c: 2})
%{b: 1, c: 2}
iex> One9.Ms.intersection(%{a: 1, b: 999, z: 999}, %{a: 3, b: 2, c: 1})
%{a: 1, b: 2}
Determine whether a value is present at all in a multiset.
Examples
iex> ms = %{"bug" => 0, "cheese" => 1}
iex> ms |> One9.Ms.member?("cheese")
true
iex> ms |> One9.Ms.member?("bug")
false
iex> ms |> One9.Ms.member?("horse")
falseSee also count_element/2.
@spec put(t(e1), e2, pos_integer()) :: t(e1 | e2) when e1: term(), e2: term()
@spec put(t_lax(e1), e2, pos_integer()) :: t_lax(e1 | e2) when e1: term(), e2: term()
Add additional copies (by default) of the element into the multiset.
Return value is well-formed whenever the input is.
Examples
iex>
@spec size(t_lax()) :: non_neg_integer()
Determine the cardinality of a multiset.
iex> One9.Ms.counts(["duck", "duck", "goose", "duck"])
...> |> One9.Ms.size()
4See also count_element/2 and empty/1.
Determine whether the first multiset is a (non-strict) subset of the second.
Either/both operands may be non-well-formed.
iex> One9.Ms.subset?(%{a: 1, b: 2}, %{a: 1, b: 3, c: 5})
true
iex> One9.Ms.subset?(%{a: 1, b: 2}, %{a: 1, b: 2})
true
iex> One9.Ms.subset?(%{a: 1, b: 2}, %{a: 1, b: 1})
false
iex> One9.Ms.subset?(%{a: 1, b: 2}, %{a: 1})
false
@spec support(t(e), :strict) :: [e] when e: term()
@spec support(t_lax(e), :lax) :: [e] when e: term()
Convert a multiset into a List of unique elements.
iex> One9.Ms.counts([1, 1, 2, 42, 42, 42, 42, 42])
...> |> One9.Ms.support()
...> |> Enum.sort()
[1, 2, 42]
@spec support_count(t_lax(), :lax) :: non_neg_integer()
@spec support_count(t(), :strict) :: non_neg_integer()
Return the cardinality of the support of a multiset.
See also size/1, to get the cardinality of the multiset itself.
Examples
iex> One9.Ms.support_count(%{a: 1, b: 99})
2
iex> One9.Ms.support_count(%{a: 99, b: 0})
1
iex> One9.Ms.support_count(%{a: 99, b: 0}, :strict) # INCORRECT
2 # Only pass :strict when you can **guarantee** the multiset is well-formed!
Examples
iex> One9.Ms.symmetric_difference(
...> %{"dog" => 10, "frog" => 1, "rat" => 100},
...> %{"cat" => 20, "frog" => 1, "rat" => 67}
...> )
%{"dog" => 10, "cat" => 20, "rat" => 33}
@spec take(t(e), e1, non_neg_integer()) :: {t(e), [e1]} when e: term(), e1: term()
iex> One9.Ms.take(%{a: 1, b: 2}, :b, 1)
{%{a: 1, b: 1}, [:b]}
iex> One9.Ms.take(%{a: 1, b: 2}, :b, 3)
{%{a: 1}, [:b, :b]}
iex> One9.Ms.take(%{a: 1, b: 2}, :z, 999)
{%{a: 1, b: 2}, []}
Convert a multiset into a complete List of elements (including repeats).
Examples
iex> One9.Ms.to_list(%{a: 1, b: 2})
...> |> Enum.sort()
[:a, :b, :b]
iex> One9.Ms.to_list([a: 1, b: 2, a: 3])
...> |> Enum.sort()
[:a, :a, :a, :a, :b, :b]
@spec union(t(e1), t(e2)) :: t(e1 | e2) when e1: term(), e2: term()
@spec union(t_lax(e1), t_lax(e2)) :: t_lax(e1 | e2) when e1: term(), e2: term()
Return the union of two multisets.
Either/both operands may be non-well-formed. Result will be well-formed whenever both operands are well-formed.
iex> One9.Ms.union(%{a: 1, b: 2}, %{b: 1, c: 3})
%{a: 1, b: 2, c: 3}