Feel.Runtime.EqualItems (feel v0.1.0)

Copy Markdown View Source

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.

KindKey
numberthe 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, nullthe value
datethe year, month and day
years and months durationthe months
days and time durationthe key of the seconds, as for a number
time, date and timethe 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)
listthe keys of the items, in order
contextthe keys of the values by name: the order of the entries is not a part of it
rangethe 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:00 is equal to 10:00:00, and that is equal to 10: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

distinct(items)

@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

occurrences(items)

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