88

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.

1

7 Answers 7

165

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());
Sign up to request clarification or add additional context in comments.

5 Comments

Also works with multiple properties: someList.GroupBy(elem=> new { elem.Prop1, elem.Prop2, elem.Prop3 }).Select(group=>group.First());
This should have been accepted answer. It does not depend on any external libraries and clean
@TejasviHegde: Well, it has downsides compared with using DistinctBy as well: 1) it takes more memory, as it builds a group for every element; 2) it can't stream the results - it has to completely read someList before it yields any elements. I would say the MoreLINQ approach is cleaner.
@JonSkeet Thanks! I didn't think from this angle :) Btw. I just went through the code of your library, I loved the coding style and neat implementation!
This really helped me a lot. we can also add .ToList() at the end. btw really helpful
86

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

11 Comments

@Jon, does it really worth to use MoreLINQ for such things instead of implementing IEqualityComparer for IEnumerable.Distinct?
@zerkms: Personally I would use MoreLINQ or Reactive Extensions, yes... it really doesn't take much effort to include it, and the calling code ends up being more readable IMO.
@Jon, I just worry about adding dependencies on 3rd party code. In other hand - built in solution obliges us to write bloated code... :-S
@Jon do you happen to know why they didn't add an overload to Distinct that takes a lambda? Same question for the overload of Contains.
@Ilya: That's easy: foo.DistinctBy(x => new { x.Prop1, x.Prop2 });
|
27

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

6 Comments

What should be happened if we call p.Distinct(); without new Comparer() parameter? In general ListOfObjects.Distinct() how works?
@ImanMahmoudinasab, in general, .NET will use equality methods(Equals and GetHashCode) from the object. If they are not defined, they would be compared by reference, therefore, an object would be equal to itself only.
How can we use Distinict , without writing " new Comparer() " as @ImanMahmoudinasab said ?
@Parsa writing distinct without comparer is easy p.Distinct();. Distinct without comparer is usefull only when you want to check two instance is same referane (same memory) not equal (diferent memory but equal property value).
@Parsa Example: var a=new Person("Iman"); var b=new Person("Iman"); var pList=new List<Person>(); pList.Add(a); pList.Add(b); pList.Add(a); in this example pList contains a twice and using pList.Distinc() will give you just one a and one b. Notice that both a and b has equal name: Iman. So pList.Distinc().Count() is 2. but with a comparer pList.Distinc(new NameComparer()).Count() is 1.
|
23

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

Comments

7

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

Comments

7

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

Comments

0

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.