Which items of a list are the first of several that are equal, and which are equal to one before them
(occurrences/1), and the list without the items that are equal to one that was kept
(distinct/1), without comparing
every pair.
Two items are equal when Feel.Runtime.Compare.equal/2 says {:ok, true}: two that cannot be compared are not equal,
and nothing is reported of them. Asking that of every pair of a list of n items is n * n / 2 comparisons, and a run
may build a list of max_list_length items. An item that has a key, a term that two items share exactly when they
are equal, is told from the others by it, in a map: each item is read once.
| Kind | Key |
|---|---|
| number | the sign, the coefficient without the zeros at its end, and the exponent that goes with it (1 and 1.00 are equal), or 0 |
string, boolean, null | the value |
| date | the year, month and day |
| years and months duration | the months |
| days and time duration | the key of the seconds, as for a number |
| time, date and time | the whole seconds (the resolution of =, D-54) of the local time if it has no time offset or zone, and of the instant if it has one (Feel.Runtime.Temporal.seconds/1) |
| list | the keys of the items, in order |
| context | the keys of the values by name: the order of the entries is not a part of it |
| range | the keys of the endpoints, and whether each is included, and how the range was written (D-61) |
A list, context or range has a key if every value in it has one, to any depth. The values that have none are of three
sorts, and each is dealt with as Compare.equal/2 would deal with it:
- A value of no kind (
Feel.Runtime.Kind: a float or an atom that a host bound) is not equal to any value, itself included, for it cannot be compared. That is known without a comparison. A range that holds one is the same. A list or a context that holds one is not equal to a value that is not the same term, or does not have the same terms in it, and is equal to one that does (Compare.equal/2, D-168): it is compared with the others of its sort, as a function is. - A function is equal to a function that is the same term. The term holds the bindings it was written in, so it is not hashed: it is compared with the others of its sort, as is a list, context or range that holds one.
- A time or a date and time, if the list has both ones with a time offset or a zone and ones with none. A time
with a zone is equal to another by their instants, and one with none to either by its local fields, so
10:00:00+01:00is equal to10:00:00, and that is equal to10:00:00Z, and the first is not equal to the third: no key is shared by all that are equal. Such a value is compared with the others of its sort, as is a list, context or range that holds one. A list that has the one sort of times only has a key for each.
The items that are compared are compared in pairs, n * n / 2 for n of them, and with each other only: an item that
has a key is never equal to one without, since the two are not of one kind or have a part that is not. So a list of
functions is as long to work through as ever it was, and a list of numbers with one function in it is not.
A term that is not a value of its kind has no key and may be equal to a value that has one (a duration whose
months are not an integer, 1.0 == 1; a date of another calendar than the ISO one). One such term anywhere in the
list makes it compared in pairs, all of it, so that the answer is the one of Compare.equal/2 for any list.
Summary
Functions
The items that are not equal to an item that was kept before them: the first of each group of equal items, in order.
What each item is, in order: :again if an item before it is equal, else :first if an item after it is, else
:alone. So :first and :alone items are the distinct values of the list, and the :first ones those of them that
occur more than once.
Functions
@spec distinct([Feel.Value.t()]) :: [Feel.Value.t()]
The items that are not equal to an item that was kept before them: the first of each group of equal items, in order.
It is the items of occurrences/1 that are not :again, but for the times that have no key, and so are compared: an
item is left out for an item that was kept, and not for one that was left out. With a and b equal, and b and
c, and a and c not equal (a time without a zone, one with an offset, and one with another), [a, b, c] has a
and c, where c is :again for occurrences/1, being equal to b.
iex> Feel.Runtime.EqualItems.distinct([Decimal.new(1), nil, Decimal.new("1.0"), nil, 1.5, 1.5])
[Decimal.new(1), nil, 1.5, 1.5]
iex> {:ok, floating} = Feel.Value.Time.parse("10:00:00")
iex> {:ok, plus_one} = Feel.Value.Time.parse("10:00:00+01:00")
iex> {:ok, utc} = Feel.Value.Time.parse("09:00:00Z")
iex> Feel.Runtime.EqualItems.distinct([floating, plus_one, utc]) == [floating, utc]
true
@spec occurrences([Feel.Value.t()]) :: [:alone | :first | :again]
What each item is, in order: :again if an item before it is equal, else :first if an item after it is, else
:alone. So :first and :alone items are the distinct values of the list, and the :first ones those of them that
occur more than once.
iex> numbers = [Decimal.new(1), Decimal.new("2.0"), Decimal.new("1.00"), Decimal.new(3)]
iex> Feel.Runtime.EqualItems.occurrences(numbers)
[:first, :alone, :again, :alone]
iex> Feel.Runtime.EqualItems.occurrences([nil, "a", nil])
[:first, :alone, :again]
iex> Feel.Runtime.EqualItems.occurrences([1.5, 1.5])
[:alone, :alone]