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


313

我有一个类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

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


Prasad

您也可以var set = new HashSet<int>(); var uniques = items.Where(x => set.Add(x.Id));。它应该是刑事这样做..
nawfal

Answers:


394
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());

嗨,克里斯蒂安,如果我有一个List <my_Custom_Class>和List <string>,代码的变化将是什么。我的自定义类具有各种项目,其中一个是DCN编号,而list <string>仅具有DCN编号。因此,我需要检查List <Custom_Class>是否包含List <string>中的任何dcn。例如,假设List1 = List <Custom_Class>和List2 = List <String>。如果List1有2000个项目,而list2有40000个项目,则List2中存在List1中的600个项目。所以在这种情况下,我需要1400作为list1的输出List。那么表达式是什么。在此先感谢

由于List1包含各种项目,因此这里还有另外一种情况,其他项目的值可能不同,但DCN必须相同。因此,就我而言,Distinct未能给出理想的结果。

2
我发现比较器类非常有用。除了简单的属性名称比较之外,它们还可以表达逻辑。我上个月写了一篇新书,做了一些做不到的事情GroupBy
克里斯蒂安·海特

运行良好,让我学到了一些新知识并研究了C#中的XoR运算符^。曾经在VB.NET中使用Xor过,但必须对代码进行两次检查才能看到它的初衷。
atconway 2014年

这是我尝试使用Distinct比较器时遇到的错误:“ LINQ to Entities无法识别方法'System.Linq.IQueryable 1[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])”方法,和这种方法不能被翻译成表达商店。
user8128167

600
var distinctItems = items.GroupBy(x => x.Id).Select(y => y.First());

28
谢谢-一直在寻找避免编写一个比较器类,所以我很高兴这个作品:)

8
+1该解决方案甚至可以平局:用条件消除重复项!
Adriano Carneiro 2013年

4
但是有点开销!
Amirhossein Mehrvarzi 2014年

1
但是,正如Victor Juri所建议的那样:请使用FirstorDefault。不敢相信,解决方案是如此简单(无需自定义相等比较器)
Cyber​​Hawk 2015年

6
您可以使用多个属性进行分组:List <XYZ> MyUniqueList = MyList.GroupBy(x => new {x.Column1,x.Column2})。Select(g => g.First())。ToList();
Sumit Joshi

40

如果您的Distinct查询有问题,您可能需要查看MoreLinq并使用DistinctBy运算符并通过id选择不同的对象。

var distinct = items.DistinctBy( i => i.Id );

1
Linq没有DistinctBy()方法。
Fereydoon Barikzehy

7
@FereydoonBarikzehy但是他不是在谈论纯粹的Linq。在邮寄中是MoreLinq项目的linq ...
Ademar

30

这就是我与Linq进行分组的方式。希望能帮助到你。

var query = collection.GroupBy(x => x.title).Select(y => y.FirstOrDefault());

3
@nawfal,我建议使用FirstOrDefault()代替First()
sobelito 2014年

23
如果我是正确的FirstOrDefault话,在Select紧随其后的地方使用此处没有任何好处GroupBy,因为不可能有一个空的组(这些组只是从集合的内容派生而来)
Roy Tinker

17

使用Distinct()但请记住,它使用默认的相等比较器比较值,因此,如果您需要任何其他功能,则需要实现自己的比较器。

有关示例,请参见http://msdn.microsoft.com/zh-cn/library/bb348436.aspx


我应该注意,如果集合成员类型是值类型之一,则默认比较器将起作用。但是csc选择哪个默认的相等比较器作为引用类型。引用类型必须具有自己的比较器。
Nuri YILMAZ'3

16

您可以在此处使用三个选项来删除列表中的重复项:

  1. 使用AA自定义相等比较,然后使用Distinct(new DistinctItemComparer())作为@Christian艾泰提及。
  2. 使用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();

11

通用扩展方法:

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);

非常干净的方法
Steven Ryssaert

4

试用此扩展方法。希望这可以有所帮助。

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);

3
List<Employee> employees = new List<Employee>()
{
    new Employee{Id =1,Name="AAAAA"}
    , new Employee{Id =2,Name="BBBBB"}
    , new Employee{Id =3,Name="AAAAA"}
    , new Employee{Id =4,Name="CCCCC"}
    , new Employee{Id =5,Name="AAAAA"}
};

List<Employee> duplicateEmployees = employees.Except(employees.GroupBy(i => i.Name)
                                             .Select(ss => ss.FirstOrDefault()))
                                            .ToList();

0

另一个解决方法,不是漂亮的购买可行。

我有一个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);
        }

-1

当您不想编写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; }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.