Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the idiomatic way to compare two lists for equality?

I have two lists I need to check whether their elements are equal (not shallow check, for elements I might rely on Kernel.==/2.)

Currently, I have:

[l1 -- l2] ++ [l2 -- l1] == []

It looks a bit overcomplicated and not quite idiomatic to me. Am I missing something? Is there a better way to compare two lists for equality?

like image 533
Aleksei Matiushkin Avatar asked Dec 07 '17 11:12

Aleksei Matiushkin


People also ask

How do you compare 2 collections to know if they are identical or not?

The isEqualCollection() method returns true when the two collections contain exact same elements with exactly the same cardinalities.

How do you know if two lists are equal in darts?

We shall write a function areListsEqual() to check if the given two lists are equal element by element. As a pre-requisite, we check if both the given variables are lists, and then compare their lengths. list1 and list2 are equal. list1 and list3 are not equal.

How do you check if two lists of strings are equal in Java?

You can compare two array lists using the equals() method of the ArrayList class, this method accepts a list object as a parameter, compares it with the current object, in case of the match it returns true and if not it returns false.


2 Answers

The shortest way I can think of is to sort the lists and compare them:

Enum.sort(l1) == Enum.sort(l2)

This will run in O(n log n) time instead of O(n ^ 2) for your Kernel.--/2 based solution.

We can't use a plain Set data structure here since the list can contain duplicates and their counts must be kept track of. We can use a Map which counts the frequency of each element and then to compare them:

iex(1)> l1 = ~w|a a b|a
[:a, :a, :b]
iex(2)> l2 = ~w|a b|a
[:a, :b]
iex(3)> m1 = l1 |> Enum.reduce(%{}, fn x, acc -> Map.update(acc, x, 1, & &1 + 1) end)
%{a: 2, b: 1}
iex(4)> m2 = l2 |> Enum.reduce(%{}, fn x, acc -> Map.update(acc, x, 1, & &1 + 1) end)
%{a: 1, b: 1}
iex(5)> m1 == m2
false
iex(6)> l2 = ~w|a b a|a
[:a, :b, :a]
iex(7)> m2 = l2 |> Enum.reduce(%{}, fn x, acc -> Map.update(acc, x, 1, & &1 + 1) end)
%{a: 2, b: 1}
iex(8)> m1 == m2
true

This is also O(n log n) so you may want to benchmark the two solutions with the kind of data you'll have to see which performs better.

like image 58
Dogbert Avatar answered Oct 12 '22 21:10

Dogbert


Dogbert's solution is apt, but it is still in Orcish.

In Erlang this would be:

lists:sort(List1) == lists:sort(List2)

A deep comparison looks very nearly the same, but of course must traverse the structures within each list.

One factor to consider is that very often the ordering of a list is its meaning. Strings are a good example, so are player ranks, start positions, game hotkey locations, and last-used-longest-kept cache lists. So do keep in mind the meaning of the comparison: "Do these two lists contain the same number and type of things?" is not the same as "Are these two lists semantically identical?"

Consider comparing anagrams:

lists:sort("AT ERR GET VICE") == lists:sort("IT ERR ETC GAVE")

That works fine as an anagram comparison, but not at all as a semantic one.

like image 34
zxq9 Avatar answered Oct 12 '22 19:10

zxq9