使用 linq 删除列表中的重复项

Posted

技术标签:

【中文标题】使用 linq 删除列表中的重复项【英文标题】:Remove duplicates in the list using linq 【发布时间】:2009-10-22 11:48:26 【问题描述】:

我有一个Itemsproperties (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。不敢相信,这个解决方案可以这么简单(没有自定义相等比较器) 您可以使用多个属性进行分组: List MyUniqueList = MyList.GroupBy(x => new x.Column1, x.Column2 ).Select(g=> g.First() ).ToList();【参考方案2】:
var 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,如果我有一个 List 和 List,代码会有什么变化。我的自定义类有各种项目,其中一项是 DCN 编号,而 list 只有 DCN 编号。所以我需要检查 List 是否包含来自 List 的任何 dcn。例如,假设 List1 = List 和 List2 = List。如果 List1 有 2000 个项目,而 list2 有 40000 个项目,则 List1 中的 600 个项目存在于 List2 中。所以在这种情况下,我需要 1400 作为我的输出列表作为 list1。那会是什么表情。提前致谢 这里还有一种情况,因为 List1 包含各种项目,其他项目值可能不同但 DCN 必须相同。所以在我的情况下,Distinct 未能提供理想的输出。 我发现比较器类非常有用。它们可以表达除了简单的属性名称比较之外的逻辑。上个月我写了一个新的,做一些GroupBy 做不到的事情。 效果很好,让我学到了一些新东西,并研究了 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.IQueryable1[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.在项目类中覆盖EqualGetHashCode

    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 删除列表中的重复项的主要内容,如果未能解决你的问题,请参考以下文章

使用 LINQ 拆分列表中的重复项

使用LINQ去除重复项。

Linq删除重复忽略大小写

使用 LINQ 删除列表中的项目 [重复]

如何使用 LINQ 从列表中获取重复项? [复制]

如何使用LINQ从列表中获取重复项?