使用 linq 删除列表中的重复项
Posted
技术标签:
【中文标题】使用 linq 删除列表中的重复项【英文标题】:Remove duplicates in the list using linq 【发布时间】:2009-10-22 11:48:26 【问题描述】:我有一个Items
和properties (Id, Name, Code, Price)
的课程。
Items
的列表中填充了重复项。
例如:
1 Item1 IT00001 $100
2 Item2 IT00002 $200
3 Item3 IT00003 $150
1 Item1 IT00001 $100
3 Item3 IT00003 $150
如何?
【问题讨论】:
我在 Items 类中还有另一个类作为属性 你也可以var set = new HashSet<int>(); var uniques = items.Where(x => set.Add(x.Id));
。这样做应该是犯罪行为..
【参考方案1】:
var distinctItems = items.GroupBy(x => x.Id).Select(y => y.First());
【讨论】:
谢谢 - 想避免写一个比较器类,所以我很高兴这个工作:) +1 这个解决方案甚至允许决胜局:使用标准消除重复! 不过有点开销! 但是,正如 Victor Juri 下面建议的那样:使用 FirstorDefault。不敢相信,这个解决方案可以这么简单(没有自定义相等比较器) 您可以使用多个属性进行分组: Listvar distinctItems = items.Distinct();
要仅匹配某些属性,请创建自定义相等比较器,例如:
class DistinctItemComparer : IEqualityComparer<Item>
public bool Equals(Item x, Item y)
return x.Id == y.Id &&
x.Name == y.Name &&
x.Code == y.Code &&
x.Price == y.Price;
public int GetHashCode(Item obj)
return obj.Id.GetHashCode() ^
obj.Name.GetHashCode() ^
obj.Code.GetHashCode() ^
obj.Price.GetHashCode();
然后像这样使用它:
var distinctItems = items.Distinct(new DistinctItemComparer());
【讨论】:
嗨 Christian,如果我有一个 ListGroupBy
做不到的事情。
效果很好,让我学到了一些新东西,并研究了 C# 中的 XoR
运算符 ^
。曾通过Xor
在 VB.NET 中使用过,但必须对您的代码进行双重检查才能看到它最初是什么。
这是我尝试使用 Distinct Comparer 时遇到的错误:“LINQ to Entities 无法识别方法 'System.Linq.IQueryable1[DataAccess.HR.Dao.CCS_LOCATION_TBL] Distinct[CCS_LOCATION_TBL](System.Linq.IQueryable
1[DataAccess.HR.Dao.CCS_LOCATION_TBL] , System.Collections.Generic.IEqualityComparer`1[DataAccess.HR.Dao.CCS_LOCATION_TBL])' 方法,并且该方法不能翻译成商店表达式。【参考方案3】:
如果您的 Distinct 查询有问题,您可能需要查看 MoreLinq 并使用 DistinctBy 运算符并按 id 选择不同的对象。
var distinct = items.DistinctBy( i => i.Id );
【讨论】:
Linq 没有 DistinctBy() 方法。 @FereydoonBarikzehy 但他不是在谈论纯 Linq。在帖子中是 linq to MoreLinq 项目...【参考方案4】:这就是我能够与 Linq 进行分组的方式。希望对您有所帮助。
var query = collection.GroupBy(x => x.title).Select(y => y.FirstOrDefault());
【讨论】:
@nawfal,我建议用 FirstOrDefault() 代替 First() 如果我是正确的,如果Select
紧跟在GroupBy
之后,则在此处使用FirstOrDefault
没有任何好处,因为不可能有一个空组(这些组只是源自集合的内容)【参考方案5】:
一种通用的扩展方法:
public static class EnumerableExtensions
public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> enumerable, Func<T, TKey> keySelector)
return enumerable.GroupBy(keySelector).Select(grp => grp.First());
使用示例:
var lstDst = lst.DistinctBy(item => item.Key);
【讨论】:
非常干净的方法 谢谢,这正是我想要的,效果很好。【参考方案6】:您可以在这里使用三个选项来删除列表中的重复项:
-
使用自定义的相等比较器,然后使用
Distinct(new DistinctItemComparer())
,正如提到的 @Christian Hayter。
使用GroupBy
,但请注意在GroupBy
中您应该按所有列分组,因为如果您只是按Id
分组,它不会始终删除重复项。例如考虑以下示例:
List<Item> a = new List<Item>
new Item Id = 1, Name = "Item1", Code = "IT00001", Price = 100,
new Item Id = 2, Name = "Item2", Code = "IT00002", Price = 200,
new Item Id = 3, Name = "Item3", Code = "IT00003", Price = 150,
new Item Id = 1, Name = "Item1", Code = "IT00001", Price = 100,
new Item Id = 3, Name = "Item3", Code = "IT00003", Price = 150,
new Item Id = 3, Name = "Item3", Code = "IT00004", Price = 250
;
var distinctItems = a.GroupBy(x => x.Id).Select(y => y.First());
此分组的结果将是:
Id = 1, Name = "Item1", Code = "IT00001", Price = 100
Id = 2, Name = "Item2", Code = "IT00002", Price = 200
Id = 3, Name = "Item3", Code = "IT00003", Price = 150
这是不正确的,因为它将Id = 3, Name = "Item3", Code = "IT00004", Price = 250
视为重复。所以正确的查询是:
var distinctItems = a.GroupBy(c => new c.Id , c.Name , c.Code , c.Price)
.Select(c => c.First()).ToList();
3.在项目类中覆盖Equal
和GetHashCode
:
public class Item
public int Id get; set;
public string Name get; set;
public string Code get; set;
public int Price get; set;
public override bool Equals(object obj)
if (!(obj is Item))
return false;
Item p = (Item)obj;
return (p.Id == Id && p.Name == Name && p.Code == Code && p.Price == Price);
public override int GetHashCode()
return String.Format("0|1|2|3", Id, Name, Code, Price).GetHashCode();
那么你可以这样使用它:
var distinctItems = a.Distinct();
【讨论】:
【参考方案7】:使用Distinct()
,但请记住它使用默认的相等比较器来比较值,因此如果您想要除此之外的任何内容,则需要实现自己的比较器。
请参阅http://msdn.microsoft.com/en-us/library/bb348436.aspx 获取示例。
【讨论】:
我应该注意到,如果集合成员类型是值类型之一,则默认比较器可以工作。但是 csc 为引用类型选择哪个默认相等比较器。引用类型必须有自己的比较器。【参考方案8】:试试这个扩展方法。希望这会有所帮助。
public static class DistinctHelper
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
var identifiedKeys = new HashSet<TKey>();
return source.Where(element => identifiedKeys.Add(keySelector(element)));
用法:
var outputList = sourceList.DistinctBy(x => x.TargetProperty);
【讨论】:
【参考方案9】:List<Employee> employees = new List<Employee>()
new EmployeeId =1,Name="AAAAA"
, new EmployeeId =2,Name="BBBBB"
, new EmployeeId =3,Name="AAAAA"
, new EmployeeId =4,Name="CCCCC"
, new EmployeeId =5,Name="AAAAA"
;
List<Employee> duplicateEmployees = employees.Except(employees.GroupBy(i => i.Name)
.Select(ss => ss.FirstOrDefault()))
.ToList();
【讨论】:
【参考方案10】:另一种解决方法,不美观购买可行。
我有一个 XML 文件,其中包含一个名为“MEMDES”的元素,它具有两个属性“GRADE”和“SPD”来记录 RAM 模块信息。 SPD中有很多重复项。
所以这是我用来删除重复项的代码:
IEnumerable<XElement> MList =
from RAMList in PREF.Descendants("MEMDES")
where (string)RAMList.Attribute("GRADE") == "DDR4"
select RAMList;
List<string> sellist = new List<string>();
foreach (var MEMList in MList)
sellist.Add((string)MEMList.Attribute("SPD").Value);
foreach (string slist in sellist.Distinct())
comboBox1.Items.Add(slist);
【讨论】:
【参考方案11】:当您不想编写 IEqualityComparer 时,可以尝试以下操作。
class Program
private static void Main(string[] args)
var items = new List<Item>();
items.Add(new Item Id = 1, Name = "Item1");
items.Add(new Item Id = 2, Name = "Item2");
items.Add(new Item Id = 3, Name = "Item3");
//Duplicate item
items.Add(new Item Id = 4, Name = "Item4");
//Duplicate item
items.Add(new Item Id = 2, Name = "Item2");
items.Add(new Item Id = 3, Name = "Item3");
var res = items.Select(i => new i.Id, i.Name)
.Distinct().Select(x => new Item Id = x.Id, Name = x.Name).ToList();
// now res contains distinct records
public class Item
public int Id get; set;
public string Name get; set;
【讨论】:
以上是关于使用 linq 删除列表中的重复项的主要内容,如果未能解决你的问题,请参考以下文章