Willem
Willem

Reputation: 9476

How to get a distinct list from a List of objects?

I have a List<MyClass> someList.

class MyClass
{
    public int Prop1...
    public int Prop2...
    public int Prop3...
}

I would like to know how to get a new distinct List<MyClass> distinctList from List<MyClass> someList, but only comparing it to Prop2.

Upvotes: 88

Views: 152980

Answers (7)

hngr18
hngr18

Reputation: 857

Since the introduction of value tuples, if you want a LINQ equivalent to SQL's DISTINCT

items.GroupBy(item => (item.prop1, item.prop2, ...)).Select(group => group.First())

Upvotes: 7

raddevus
raddevus

Reputation: 9077

I know it's been a while, but I needed the simplest answer and at this time (with .NET 4.5.1) I found the following to be the most straight-forward answer I could get to:

IEnumerable<long> allIds = waitingFiles.Values.Select(wf => wf.groupId).Distinct();

My situation is that I have a ConcurrentDictionary that looks something like: ConcurrentDictionary<long, FileModel>

The ConcurrentDictionary Values property is basically my List<FileModel>.

*FileModel has a groupId that isn't necessarily unique (though, obviously the key (long) that I use to add the FileModel object into the dictionary is unique to the FileModel).

*Named for clarity in the example.

The point is that I have a large number of FileModels (imagine 100) in the ConcurrentDictionary and within those 100 FileModels there are 5 different groupIds.

At this point I just need a list of the distinct groupId.

So, again if I just had a list of FileModel the code would look like the following:

IEnumerable <long> allIds = allFileModel.Select(fm => fm.groupId).Distinct();

Upvotes: 0

Masoud Darvishian
Masoud Darvishian

Reputation: 3964

If you would like to Distinct your list by multiple fields, You have to create an instance of IEqualityComparer interface:

public class MyComparer : IEqualityComparer<MyModel>
{
    public bool Equals(MyModel x, MyModel y)
    {
       // compare multiple fields
        return
            x.Field1 == y.Field1 &&
            x.Field2 == y.Field2 &&
            x.Field3 == y.Field3 ;
    }

    public int GetHashCode(MyModel obj)
    {
        return 
            obj.Field1.GetHashCode() + 
            obj.Field2.GetHashCode() + 
            obj.Field3.GetHashCode();
    }
}

Then use the comparer to distinct your list:

var distinctedList = myList.Distinct(new MyComparer()).ToList();

Upvotes: 7

Jon Skeet
Jon Skeet

Reputation: 1499800

Unfortunately there's no really easy built-in support for this in the framework - but you can use the DistinctBy implementation I have in MoreLINQ.

You'd use:

var distinctList = someList.DistinctBy(x => x.Prop2).ToList();

(You can take just the DistinctBy implementation. If you'd rather use a Microsoft implementation, I believe there's something similar in the System.Interactive assembly of Reactive Extensions.)

Upvotes: 86

Waldemar Gałęzinowski
Waldemar Gałęzinowski

Reputation: 1233

Override Equals(object obj) and GetHashCode() methods:

class MyClass
{
    public int Prop1 { get; set; }
    public int Prop2 { get; set; }
    public int Prop3 { get; set; }

    public override bool Equals(object obj)
    {
        return ((MyClass)obj).Prop2 == Prop2;
    }
    public override int GetHashCode()
    {
        return Prop2.GetHashCode();
    }
}

and then just call:

List<MyClass> distinctList = someList.Distinct().ToList();

Upvotes: 23

Ilya Smagin
Ilya Smagin

Reputation: 6152

you need to use .Distinct(..); extension method. Here's a quick sample:

public class Comparer : IEqualityComparer<Point>
    {
        public bool Equals(Point x, Point y)
        {
            return x.X == y.X;
        }

        public int GetHashCode(Point obj)
        {
            return (int)obj.X;
        }
    }

Do not forget about GetHashCode.

Usage:

List<Point> p = new List<Point>();
// add items
p.Distinct(new Comparer());

Upvotes: 27

CodesInChaos
CodesInChaos

Reputation: 108790

You can emulate the effect of DistinctBy using GroupBy and then just using the first entry in each group. Might be a bit slower that the other implementations though.

someList.GroupBy(elem=>elem.Prop2).Select(group=>group.First());

Upvotes: 165

Related Questions