简体   繁体   English

保留重复项的两个列表之间的差异

[英]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. 该列表是list1所有元素从list2删除的地方我不认为有Linq扩展方法,因为Except会删除重复项。

The non-linq way to do this would be: 这样做的非linq方法是:

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. 但我想知道是否有我可能错过的Linq扩展方法。

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)

Looking at your example, I think you mean "all the elements from list2 removed from list1": 看您的示例,我认为您的意思是“ list2中的所有元素已从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;

Not LINQ, but a one-line anyway: 不是LINQ,而是单行:

list2.ForEach(l => list1.Remove(l));

btw... it would be nice if List<int> had something like AddRange but to remove a bunch of items at the same time. 顺便说一句...如果List<int>具有类似AddRange但同时删除一堆项目, AddRange了。

If you do not care about the order in which the elements of the result come, you can do it with LINQ's GroupBy : 如果您不关心结果元素的顺序,可以使用LINQ的GroupBy

var a = new List<string>{"A","A", "B", "C"};
var b = new List<string>{"A", "B"};
var res  =  a.Select(e => new {Key=e, Val=1})
    .Concat(b.Select(e => new {Key=e, Val=-1}))
    .GroupBy(e => e.Key, e => e.Val)
    .SelectMany(g => Enumerable.Repeat(g.Key, Math.Max(0, g.Sum())))
    .ToList();

Here is a demo on ideone . 这是有关ideone演示

I must admit that your solution is far simpler than mine, so it should be regarded as a mere curiosity, a way to prove that this could be done with LINQ too. 我必须承认,您的解决方案比我的解决方案简单得多,因此应该将其视为纯粹的好奇心,这是一种证明也可以使用LINQ完成的方法。

Here is how it works: for each element from the first list we add a key-value pair with a 1 ; 它是这样工作的:对于第一个列表中的每个元素,我们添加一个带有1的键值对; for each element from the second list we add a key-value pair with -1 . 对于第二个列表中的每个元素,我们添加一个-1的键-值对。 Then we group all elements by their key, total up their ones and negative ones, and produce as many keys as the total, making sure that we do not select anything when the result is negative. 然后,我们将所有元素按其键分组,将它们的总和与否相加,并产生与总数相同的键,确保结果为负时不选择任何内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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