保留重复项的两个列表之差
本文关键字:两个 列表 保留 | 更新日期: 2023-09-27 18:07:15
我有两个列表:
var list1 = new List<string> { "A", "A", "B", "C" };
var list2 = new List<string> { "A", "B" };
,我想生成一个像
这样的列表var result = new[] { "A", "C" };
其中列表是从list2
中删除的list1
中的所有元素,我不认为有Linq扩展方法,因为Except
删除重复项。
这样做的非linq方式是:
var tempList = list1.ToList();
foreach(var item in list2)
{
tempList.Remove(item);
}
但我想知道是否有一个Linq扩展方法,我可能错过了。
编辑:因为可能没有,所以我做了一个扩展方法。
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;
}
}
用法:
list1.RemoveRange(list2)
看看你的例子,我想你的意思是"从list1中删除list2中的所有元素":
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;
不是LINQ,而是一行:
list2.ForEach(l => list1.Remove(l));
顺便说一句……如果List<int>
有类似AddRange
的东西,但同时删除一堆项目,那就太好了。
如果您不关心结果元素出现的顺序,您可以使用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();
这是一个ideone的演示。
我必须承认你的解决方案比我的简单得多,所以它应该被看作是一种纯粹的好奇心,一种证明用LINQ也可以做到这一点的方法。
下面是它的工作原理:对于第一个列表中的每个元素,我们添加一个具有1
的键值对;对于第二个列表中的每个元素,我们用-1
添加一个键值对。然后我们按键对所有元素进行分组,将它们的1和负的键加起来,并生成与总数相同的键,确保当结果为负时我们不选择任何内容。