简体   繁体   中英

Find the number of differences between two lists

I want to compare two lists with the same number of elements, and find the number of differences between them. Right now, I have this code (which works):

public static int CountDifferences<T> (this IList<T> list1, IList<T> list2)
{
    if (list1.Count != list2.Count)
        throw new ArgumentException ("Lists must have the same number of elements", "list2");

    int count = 0;
    for (int i = 0; i < list1.Count; i++) {
        if (!EqualityComparer<T>.Default.Equals (list1[i], list2[i]))
            count++;
    }

    return count;
}

This feels messy to me, and it seems like there must be a more elegant way to achieve it. Is there a way, perhaps, to combine the two lists into a single list of tuples, then simple examine each element of the new list to see if both elements are equal?

Since order in the list does count this would be my approach:

public static int CountDifferences<T>(this IList<T> list1, IList<T> list2)
{
    if (list1.Count != list2.Count)
        throw new ArgumentException("Lists must have the same number of elements", "list2");

    int count  = list1.Zip(list2, (a, b) => a.Equals(b) ? 0 : 1).Sum();
    return count;
}

Simply merging the lists using Enumerable.Zip() then summing up the differences, still O(n) but this just enumerates the lists once.

Also this approach would work on any two IEnumerable of the same type since we do not use the list indexer (besides obviously in your count comparison in the guard check).

Try something like this:

var result = list1.Intersect(list2);
var differences = list1.Count - result.Count();

If order counts:

var result = a.Where((x,i) => x !=b[i]);
var differences = result.Count();

I think your approach is fine, but you could use LINQ to simplify your function:

public static int CountDifferences<T>(this IList<T> list1, IList<T> list2)
{
    if(list1.Count != list2.Count)
        throw new ArgumentException("Lists must have same # elements", "list2");
    return list1.Where((t, i) => !Equals(t, list2[i])).Count();
}

The way you have it written in the question, I don't think Intersect does what you're looking for. For example, say you have:

var list1 = new List<int> { 1, 2, 3, 4, 6, 8 };
var list2 = new List<int> { 1, 2, 4, 5, 6, 8 };

If you run list1.CountDifferences(list2) , I'm assuming that you want to get back 2 since elements 2 and 3 are different. Intersect in this case will return 5 since the lists have 5 elements in common. So, if you're looking for 5 then Intersect is the way to go. If you're looking to return 2 then you could use the LINQ statement above.

You can use the extension method Zip of List.

List<int> lst1 = new List<int> { 1, 2, 3, 4, 5 };
List<int> lst2 = new List<int> { 6, 2, 9, 4, 5 };
int cntDiff = lst1.Zip(lst2, (a, b) => a != b).Count(a => a);

// Output is 2

You want the Intersect extension method of Enumerable.

public static int CountDifferences<T> (this IList<T> list1, IList<T> list2)
{
    if (list1.Count != list2.Count)
        throw new ArgumentException ("Lists must have the same number of elements", "list2");

    return list1.Count - list1.Intersect(list2).Count();
} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM