使用LINQ在列表中去除重复项

22
假设您有如下的 MyObject 列表:
public class MyObject
{
  public int ObjectID {get;set;}
  public string Prop1 {get;set;}
}

如何从一个列表中移除重复项,其中可能存在多个具有相同ObjectID的对象实例。

谢谢。


为什么会有具有相同ID的对象(通常为了这个原因而保持不同...)? 我想你可以创建一个ID和字符串的列表(作为组合字符串),然后执行.Distinct(),但如果ID是非唯一的,则认为存在更基本的问题。 - soandos
@soandos:他可能会无意中在列表中获取相同的对象两次。 - Jay Sullivan
@notfed:这仍然表明出现了某些问题。 - soandos
但是,如果出现问题的是外部来源,则需要删除重复项。 - dragoncmd
3个回答

50
你可以使用 GroupBy() 并选择每个组的第一个项来实现你想要的功能 - 假设你想为每个不同的 ObjectId 属性选择一个项:
var distinctList = myList.GroupBy(x => x.ObjectID)
                         .Select(g => g.First())
                         .ToList();

MoreLinq项目中还有DistinctBy(),可以使用更简洁的语法(但会向你的项目添加依赖项):

var distinctList = myList.DistinctBy( x => x.ObjectID).ToList();

好的,很棒,就是这样了。myList = myList...也可以吗?只是想避免创建一个新列表。 - frenchie
@frenchie:当然可以,你可以将同一个列表变量重新赋值。 - BrokenGlass
当使用LINQ时,您无法避免创建新列表。如果要使用相同的列表,则必须自行修改它,使用list.Remove(item)list.RemoveAt(index) - Zebi
使用类似MoreLinq这样的东西有什么缺点吗? - rollsch
不,只是添加外部依赖的复杂性成本。 - BrokenGlass
显示剩余3条评论

12

你可以使用Distinct()方法来实现此操作。但是,由于该方法使用默认的相等比较器,因此你的类需要像这样实现IEquatable<MyObject>

public class MyObject : IEquatable<MyObject>
{
    public int ObjectID {get;set;}
    public string Prop1 {get;set;}

    public bool Equals(MyObject other)
    {
        if (other == null) return false;
        else return this.ObjectID.Equals(other.ObjectID); 
    }

    public override int GetHashCode()
    {
        return this.ObjectID.GetHashCode();
    }
}

现在你可以使用 Distinct() 方法:

List<MyObject> myList = new List<MyObject>();
myList.Add(new MyObject { ObjectID = 1, Prop1 = "Something" });
myList.Add(new MyObject { ObjectID = 2, Prop1 = "Another thing" });
myList.Add(new MyObject { ObjectID = 3, Prop1 = "Yet another thing" });
myList.Add(new MyObject { ObjectID = 1, Prop1 = "Something" });

var duplicatesRemoved = myList.Distinct().ToList();

那会让代码看起来更加优雅!而且易于阅读。 :-) - Juan Gomez
为什么这比上面提供的三行答案更好?它有什么额外的功能? - frenchie
它使您的对象可以进行比较。这也将需要一些其他列表功能,并且具有ID(实体)的对象应该根据其ID进行比较。 - Zebi
1
@frenchie:我认为这种方法比“GroupBy”解决方案更加优雅和易读,而且不像“DistinctBy”解决方案那样需要添加额外的库。 - Kristof Claes
当我使用这个方法并使用foreach循环调试列表时,仍然会得到重复项。请参见我的问题:http://stackoverflow.com/questions/42316343/groupby-to-remove-duplicates-from-ienumerable-list-of-objects - naz786

3

您可以通过实现IEqualityComparer接口来创建自定义对象比较器:

public class MyObject
{
    public int Number { get; set; }
}

public class MyObjectComparer : IEqualityComparer<MyObject>
{
    public bool Equals(MyObject x, MyObject y)
    {
        return x.Id == y.Id;
    }

    public int GetHashCode(MyObject obj)
    {
        return obj.Id.GetHashCode();
    }
}

那么只需要简单地执行以下步骤:
myList.Distinct(new MyObjectComparer()) 

尝试过这个方法,但仍然返回了重复的列表。 - naz786

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接