Search code examples
c#linq

Difference between two lists preserving duplicates


I have two lists:

var list1 = new List<string> { "A", "A", "B", "C" };
var list2 = new List<string> { "A", "B" };

and I would like to produce a list like

var result = new[] { "A", "C" };

Where the list is all the elements from list1 removed from list2 I don't think there is a Linq extension method for this since Except removes duplicates.

The non-linq way to do this would be:

var tempList = list1.ToList();
foreach(var item in list2)
{
    tempList.Remove(item);
}

but I am wondering if there is a Linq extension method that I might have missed.

Edit:

Since there probably aren't any here's an extension method I made.

public static class LinqExtensions
{
    public static IEnumerable<T> RemoveRange<T>(this IEnumerable<T> source, IEnumerable<T> second)
    {
        var tempList = source.ToList();
            
        foreach(var item in second)
        {
            tempList.Remove(item);
        }
        
        return tempList;
    }
    
    public static IEnumerable<TFirst> RemoveMany<TFirst, TSecond>(this IEnumerable<TFirst> source, IEnumerable<TSecond> second, Func<TSecond, IEnumerable<TFirst>> selector)
    {
        var tempList = source.ToList();
            
        foreach(var item in second.SelectMany(selector))
        {
            tempList.Remove(item);
        }
        
        return tempList;
    }
}

Usage:

list1.RemoveRange(list2)

Solution

  • Looking at your example, I think you mean "all the elements from list2 removed from list1":

    var lookup2 = list2.ToLookup(str => str);
    
    var result = from str in list1
                 group str by str into strGroup
                 let missingCount 
                      = Math.Max(0, strGroup.Count() - lookup2[strGroup.Key].Count())
                 from missingStr in strGroup.Take(missingCount)
                 select missingStr;