How shall I Quickly compare two generic lists and it's contents for checking if they are identical?

88 Views Asked by At

//Here I have a List of Lists

List<List<T>> SelectionList = new List<List<T>>();

//My current code to compare lists

    for(int i = 0; i < SelectionList.Count; i++)
    {
        for(int j = 0; j < SelectionList.Count; j++)
        {
            if (SelectionList[i].Equals(SelectionList[j]))
            {
                SelectionList.Remove(SelectionList[j]);
            }
        }
    }

//Note: The above code supposedly works, in cases where the contents of both the lists are ordered ie., they are in same index, but in my lists they might be same but list contents are shuffled. in this case it fails to identify.

//Basically I need to remove any repetitions of same list in my list of lists;

3

There are 3 best solutions below

0
Matthew Watson On BEST ANSWER

If (and only if) the following is true:

  • Your individual lists do not contain any duplicates
  • The type T of your list elements implements IComparable and GetHashCode() correctly

Then you can remove each list that matches an earlier list like so (note that you must traverse the list backwards when removing items from the end of it otherwise the loop indices could go out of range):

for (int i = lists.Count - 1; i > 0; i--)
{
    for (int j = i - 1; j >= 0; j--)
    {
        if (!lists[i].Except(lists[j]).Any())
        {
            lists.RemoveAt(i);
            break;
        }
    }
}

The important line here is: !lists[i].Except(lists[j]).Any().

Let's break it down:

lists[i].Except(lists[j]): - This produces a sequence of all the elements of lists[i] that are NOT in lists[j], regardless of order.

Thus if all of the items in lists[j] are also in lists[j], this will produce an empty sequence; otherwise, it will produce a non-empty sequence.

The .Any() will return true for a non-empty sequence, and false for an empty sequence.

So lists[i].Except(lists[j]).Any() will return false if the items are the same in each list and true if they differ.

This is the opposite of what we want for the lists.RemoveAt() so we just negate the result, giving the final code !lists[i].Except(lists[j]).Any().

Compilable console app:

using System;
using System.Collections.Generic;
using System.Linq;

static class Program
{
    static void Main()
    {
        var lists = new List<List<int>>
        {
            new() {1, 2, 3, 4, 5}, // [0]
            new() {2, 3, 4, 5, 6}, // [1]
            new() {3, 4, 5, 6, 7}, // [2]
            new() {5, 4, 3, 2, 1}, // [3] Dupe of [0]
            new() {4, 5, 6, 7, 8}, // [4]
            new() {6, 5, 4, 3, 2}, // [5] Dupe of [1]
            new() {5, 6, 7, 8, 9}, // [6] 
            new() {3, 4, 5, 2, 1}, // [7] Dupe of [0]
            new() {6, 7, 8, 9, 0}  // [8]
        };

        for (int i = lists.Count - 1; i > 0; i--)
        {
            for (int j = i - 1; j >= 0; j--)
            {
                if (!lists[i].Except(lists[j]).Any())
                {
                    lists.RemoveAt(i);
                    break;
                }
            }
        }

        for (int i = 0; i < lists.Count; ++i)
        {
            Console.WriteLine(string.Join(", ", lists[i]));
        }
    }

Try it on DotNetFiddle: https://dotnetfiddle.net/nWnOcP

0
Xaver On

If, for each list, each possible value appears at most once in the list, you could use a Dictionary<T,int> to store how often an element appears. Then you perform the following steps:

  1. Iterate over the lists, and, for each list, do the following: For each list element k, check if the dictionary contains it as a key. If it does not, then add key k with value 1 to your dictionary. If it does, then increment the value for key k by 1.
  2. Iterate over the dictionary's elements and check that all values are 2 (if you have two lists) or n (if you have n lists).
0
Kamal24h On

Your way is not correct, as you said, try this:

you should iterate the following procedure over your list-Of-Lists;

private bool Compare(List<T> List1,List<T> List2)
{
   var infirstNotSecond = list1.Except(list2).ToList();
   var insecondNotFirst = list2.Except(list1).ToList();
   return !infirstNotSecond.Any() && !insecondNotFirst.Any();
}