C-Sharp | Java | Python | Swift | GO | WPF | Ruby | Scala | F# | JavaScript | SQL | PHP | Angular | HTML
We develop an algorithm to test for this condition. There are many possible approaches. We want the simplest. We look at some research and develop a C# solution.
Input and output List 1 contents: 1, 2, 4 List 2 contents: 2, 1, 4 Equal?: True List 1 contents: 5, 4, 6 List 2 contents: 6, 5, 4 Equal?: True List 1 contents: 1, 2, 4 List 2 contents: 1, 4 Equal?: False List 1 contents: 1, 5 List 2 contents: 2, 5 Equal?: False List 1 contents: 1, 2 List 2 contents: 1, 2 Equal?: True
Example. Before developing the solution, I researched on Google and found a variety of approaches. One solution copies both Lists to arrays, sorts them, and then loops over the elements. The best versions use Dictionary and compare frequencies.
Here: We see a generic method—it receives parameters of a caller-specified type. The syntax uses the angle brackets, < and >.
C# program that tests List equality using System; using System.Collections.Generic; class Program { static void Main() { List<int> la = new List<int>() { 1, 0, 4, 200, -40 }; List<int> lb = new List<int>() { -40, 200, 4, 1, 0 }; List<int> lc = new List<int>() { 3, 5, 4, 9, 11 }; List<int> ld = new List<int>() { 6, 6, 100 }; List<int> le = new List<int>() { 6, 100, 100 }; Console.WriteLine(UnorderedEqual(la, lb)); // true Console.WriteLine(UnorderedEqual(la, lc)); // false Console.WriteLine(UnorderedEqual(lc, ld)); // false Console.WriteLine(UnorderedEqual(ld, le)); // false int[] a1 = new int[] { 1, 2, 5 }; int[] a2 = new int[] { 2, 5, 1 }; int[] a3 = new int[] { 1, 1, 3 }; int[] a4 = new int[] { 3, 3, 1 }; Console.WriteLine(UnorderedEqual(a1, a2)); // true Console.WriteLine(UnorderedEqual(a1, a3)); // false Console.WriteLine(UnorderedEqual(a3, a4)); // false } static bool UnorderedEqual<T>(ICollection<T> a, ICollection<T> b) { // 1 // Require that the counts are equal if (a.Count != b.Count) { return false; } // 2 // Initialize new Dictionary of the type Dictionary<T, int> d = new Dictionary<T, int>(); // 3 // Add each key's frequency from collection A to the Dictionary foreach (T item in a) { int c; if (d.TryGetValue(item, out c)) { d[item] = c + 1; } else { d.Add(item, 1); } } // 4 // Add each key's frequency from collection B to the Dictionary // Return early if we detect a mismatch foreach (T item in b) { int c; if (d.TryGetValue(item, out c)) { if (c == 0) { return false; } else { d[item] = c - 1; } } else { // Not in dictionary return false; } } // 5 // Verify that all frequencies are zero foreach (int v in d.Values) { if (v != 0) { return false; } } // 6 // We know the collections are equal return true; } }
In this example, TryGetValue is used instead of ContainsKey. The TryGetValue method eliminates extra hash key computations. This enhances performance and yields a method that is faster than most.
UnorderedEqual receives ICollections and checks length. Two collections that implement ICollection<T> are received and their Counts are compared. If the two parameters don't have the same number of elements, they cannot be equal.
Note: The Dictionary used in this method is for storing each key of type T and its frequency.
And: If a specific key is found once, its frequency will be set to 1, for example.
The method places each element into the Dictionary as a key. If an element occurs twice, its frequency is incremented. TryGetValue helps avoid hash computations. Each element in the second collection is decremented in the Dictionary.
And: If any value goes below zero, we already know the collections are not equal.
Final steps. It verifies the keys. The method enforces that each key have a frequency of 0. This ensures there are no extra elements in the first collection. Here we know that the collections are equal.
Generics. I don't normally use custom generics. They add needless complexity to small programs. You can rewrite the above solution simply by changing the arguments to int[], and the Dictionary and loops to use int instead of T.
However: The implementation here will work on both strings and numeric types, with no code changes.
Summary. We saw a solution to checking unordered elements that is a generic method, which works for both string and int. It uses a more efficient Dictionary syntax that avoids excessive lookups.
Also: The solution iterates over each value in the Dictionary. This avoids many lookups.