0

I have a list of Clubs that has a list of Member property in it. How do I get a List of unique members?

Here's why this question is different than what you're suggesting as duplicate: This is a bit different than the example in that one of the properties of my class is another class -- not a simple int or string.

My class looks like this:

public class Club
{
   public int Id { get; set; }
   public string ClubName { get; set; }
   public List<Member> Members { get; set; }
}

And the member class looks like this:

public class Member
{
   public Guid Id { get; set; }
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public DateTime MemberSince { get; set; }
}

To illustrate the point a bit more...

var myClubs = new List<Club>();
myClubs = getSomeData();

var uniqueMembers = myClubs.Select()...
Sam
  • 26,817
  • 58
  • 206
  • 383
  • If the ID is the same, then it's the same member. I want to get the unique members from a List and a member can be in multiple clubs which is why I just want to get a list of List that are unique in List – Sam Feb 04 '16 at 23:40
  • 1
    @Sam There are *plenty* of examples and solutions to this. Please do a bit of research. Searching for `C# unique list` is enough to get you a bunch of examples. – Rob Feb 04 '16 at 23:41
  • @Rob You mean I shouldn't have asked? – Sam Feb 04 '16 at 23:44
  • I think you are looking for distinct: https://msdn.microsoft.com/library/bb348436%28v=vs.100%29.aspx – SamFisher83 Feb 04 '16 at 23:45
  • 1
    @Sam I mean you should have searched for an existing solution before asking this question. Otherwise, SO (and the internet in general, really) will end up polluted with problems which have been solved many times in the past, making it hard for interesting and new problems to be seen and answered. Having 100 answers saying "Use `Distinct`" isn't really that useful. A good rule of thumb, I've found, is to not ask a question before doing at least 30 minutes of research first. If you come up short after that time, you've probably got a new and interesting question on your hands. – Rob Feb 04 '16 at 23:47
  • @Drew - This isn't a duplicate. The link you gave was for producing a distinct list of strings, this is for a class. The answer here is more complex. – Enigmativity Feb 04 '16 at 23:53
  • @Enigmativity The linked question has the solution to this question as well. In particular atik sarker's answer and Dan Busha's answer. It very much is a duplicate. – Rob Feb 04 '16 at 23:55
  • Retracted, thanks. Someone consider hammering it if need be http://stackoverflow.com/questions/10255121/get-a-list-of-distinct-values-in-list – Drew Feb 04 '16 at 23:55
  • @Sam: so what is the final output that you want? A list of distinct members (`IEnumerable`)? Or a list of list of members (`IEnumerable`)? – code4life Feb 04 '16 at 23:57
  • @code4life I'm trying to a get a List that contains unique members across multiple clubs i.e. not just unique members within one club but in myClubs = new List – Sam Feb 04 '16 at 23:58
  • @Drew - Don't delete comments like that. It make it hard to understand the conversation. – Enigmativity Feb 04 '16 at 23:59
  • I placed the url in the retracted line – Drew Feb 05 '16 at 00:01

3 Answers3

2

Here is one way to do it:

var result = myClubs
    .SelectMany(x => x.Members)
    .GroupBy(x => x.Id)
    .Select(x => x.First())
    .ToList();

Based on the comments, members are equal if they have the same Id.

So, first you use SelectMany to select all members from all clubs, then you group them by the Id.

Now each group will just contain multiple instances of the same member (or just a single member if there are no duplicates of such member).

Then you would just select the first member of each group to get the unique members.

Yacoub Massad
  • 27,509
  • 2
  • 36
  • 62
0

This works:

List<Member> distinctMembers =
    myClubs
        .SelectMany(c => c.Members)
        .GroupBy(m => m.Id)
        .SelectMany(m => m.Take(1))
        .ToList();
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • Why the down-vote? This works precisely as asked for and is quite clean. – Enigmativity Feb 05 '16 at 00:01
  • This answer is semantically the same (`Take(1).SelectMany(m => m)` vs `Select(m => m.First())` as [a solution found in the proposed duplicate](http://stackoverflow.com/a/24360130/563532) you said was *not* a duplicate, and subsequently had the vote retracted. This question has been asked *so many times before* on stack overflow, and I'm really surprised the attention it's gotten and responses from high-rep users without anyone closing as dupe (and actively contesting it). – Rob Feb 05 '16 at 00:04
  • @Rob, I re-looked at it. You're right. It was a dup. – Enigmativity Feb 05 '16 at 00:06
  • Might be better to just remove this answer... plus the double flattening projections (`SelectMany`) - I am not a fan of. – code4life Feb 05 '16 at 00:11
  • @code4life - I kind of like the `.Take(1).SelectMany(m => m)` instead of the `.Select(m => m.First())` as it avoids blocking operations. I know in this case, with the `GroupBy`, that there must be at least one element, but I like to maintain the habit of not using blocking operations. – Enigmativity Feb 05 '16 at 00:20
  • @Enigmativity: Yeah, but double flattening? That's a practice that could potentially be quite expensive. Think of all the list of lists being traversed... – code4life Feb 05 '16 at 00:22
  • @code4life - It's no more that calling `.First()`. Remember that LINQ is deferred so `.Take(1).SelectMany(m => m)` is only one hit on the source, as is `.First()`. – Enigmativity Feb 05 '16 at 00:34
  • Am I the only one who thinks that `GroupBy(..).Take(1).SelectMany(m=>m)` returns the duplicate members of the first group while `GroupBy(...).Select(g => g.First())` returns the first member of every group. – Ivan Stoev Feb 05 '16 at 01:22
  • @IvanStoev - You are spot on. I made a stupid mistake. I've fixed it. – Enigmativity Feb 05 '16 at 01:31
-1

Override GetHashCode and Equals methods.

public class Member
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime MemberSince { get; set; }

    public override int GetHashCode()
    {
        // if each member has a unique Id assigned to him/her
        // you can simply return Id
        return Id.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        var member = (Member)obj;
        return member == null
            ? false
            : Id == member.Id;
    }
}

Then to get a list of unique members for each club, you can call:

var uniqueMembersForEachClub = myClubs.Select(c => c.Members.Distinct());

And to get a list of unique members for all clubs, you can call:

var uniqueMembers = myClubs.SelectMany(c => c.Members).Distinct();

You will need to include:

using System.Linq;

Good luck!

Pat
  • 59
  • 4
  • But is equality strictly based on member id only? What if we want to allow editing and member id is also an editable field (I mean, it does have a `set`, right....)? We're kind of using a sledgehammer approach here. Really, the targeted approach would be to use `IEqualityComparer`, because distinctness can be relative. – code4life Feb 05 '16 at 00:01
  • It is bad to base `GetHashCode` on properties that can change value. If it were `public Guid Id { get; private set; }` it would be a much better choice, but as it stands external code can modify the `Id` and that breaks the hash code. – Enigmativity Feb 05 '16 at 00:03
  • Nice comment by Jon Skeet on using `Guid.GetHashCode` and why that would be nonsensical. http://stackoverflow.com/questions/7326593/guid-gethashcode-uniqueness – code4life Feb 05 '16 at 00:14
  • @code4life - Are you replying to my comment? – Enigmativity Feb 05 '16 at 00:33
  • Making Id private set would be great. However, my answer was to show how to use GetHashCode and Equals methods. You don't have to implement GetHashCode the way I do (Thanks to Jon Skeet's answer, I do realize now that Guid.GetHashCode is not always unique). However, by implementing GetHashCode and Equals methods properly, you shift all the logics to the model, and make your life easier when you want to get a list of unique members (or whatever you want to do with the objects of the class). – Pat Feb 05 '16 at 02:42