10

I have a program where there is a topic (like a forum), people can react to that topic.

USER:

  1. id
  2. first name
  3. last name

TOPIC:

  1. id
  2. subject

REACTION:

  1. id
  2. topic_id
  3. content

Code:

List<USER> ListOfAllUsers = new List<USER>();
var AllReactions = from r in db.REACTIONs
                   where r.topic_id == _topic_id
                   select r;

foreach (var itemX in AllReactions)
{
    ListOfAllUsers.Add(itemX.USER);
}

//Distinct the list of duplicates
var ListOfUsers = ListOfAllUsers.Distinct().ToList();

Now, the "distinct" list still has some duplicates, how do I distinct the list based on the user id's? Or maybe there is another better way to do this. Thanks in advance.

Abbas
  • 14,186
  • 6
  • 41
  • 72
Niek Jonkman
  • 477
  • 4
  • 11
  • 23

5 Answers5

28

You can use GroupBy to achieve that

var ListOfUsers = ListOfAllUsers.GroupBy(x => x.Id)
                                  .Select(g => g.First())
                                  .ToList();
Kamil Budziewski
  • 22,699
  • 14
  • 85
  • 105
  • 2
    +1 Agree, solution is working. It should not be downvoted (especially without any comments) – Sergey Berezovskiy Dec 18 '13 at 10:49
  • @NiekJonkman keep in mind, that you will download ALL duplicated users data from database, and filter out duplicated users on client side. Also I suggest you to Include related User entity in your query to avoid additional lazy queries for each user – Sergey Berezovskiy Dec 18 '13 at 10:50
  • I am aware of that, but I have to perform 2 queries (I only described one in the OP because I only needed to know how it works) and I don't know if you can fill a list and distinct that list if you used 2 different distincted queries. – Niek Jonkman Dec 18 '13 at 10:52
  • @NiekJonkman in that case, of course grouping is solution. BTW if you will define `Equals` and `GetHashCode` methods for your `User` entity, you will be able simply call `Distinct()` method to get only distinct users from list – Sergey Berezovskiy Dec 18 '13 at 10:54
4

Distinct has an overload that receives an instance of IEqualityComparer<T>, which is an object that contains the logic that allows LINQ to know which two objects are equal, and thus one should be eliminated.

You need to implement this (very simple) interface, something like this:

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

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

And then pass an instance of UserEqualityComparer to Distinct():

var ListOfUsers = ListOfAllUsers.Distinct(new UserEqualityComparer()).ToList();
Avner Shahar-Kashtan
  • 14,492
  • 3
  • 37
  • 63
3

I suggest you to let database return distinct users for you:

    List<USER> ListOfAllUsers = 
         db.REACTIONs.Where(r => r.topic_id == _topic_id)
                     .Select(r => r.USER)
                     .Distinct()
                     .ToList();

That will be translated into single SQL query. Something like (assume your USER table has two columns - Id and Name):

SELECT 
    [Distinct1].[Id] AS [Id], 
    [Distinct1].[Name] AS [Name]
    FROM ( SELECT DISTINCT 
        [Extent2].[Id] AS [Id], 
        [Extent2].[Name] AS [Name]
        FROM  [dbo].[USER] AS [Extent1]
        INNER JOIN [dbo].[REACTION] AS [Extent2] 
            ON [Extent1].[Id] = [Extent2].[UserId]
        WHERE @id = [Extent1].[topic_id]
    )  AS [Distinct1]
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • Won't this only work if you've already properly implemented a `Equals` method for the model/User class? Otherwise `Distinct` by itself won't work against the model list. Hence the question. – Don Cheadle Apr 27 '20 at 15:25
2

MoreLinq (available on NuGet) has a DistincBy method that allow you to use a delegate as equality comparer.

So you only have to do something like this :

var ListOfUsers = ListOfAllUsers.DistinctBy(user => user.id).ToList();

Edit : MoreLinq Link

Uwy
  • 21
  • 1
0

I tried in .Net core similar code. No need to define explicit comparator. Override Equals and GetHashcode in class. No need to GroupBy. By default Distinct() works even if other props are distinct.

public class User  
    {
        public int Id { get; set; }    
        public string FirstName { get; set; }            
        public string LastName { get; set; }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(this, obj)) return true;    
            if (obj is User && (obj as User).Id == this.Id) return true;    
            return false;          
        }

        public override int GetHashCode()
        {
            int hashProductCode = Id.GetHashCode();    
            return hashProductCode;
        }    
    }

User[] users =  {
                new User {Id=1, FirstName="John", LastName="Smith" },
                new User {Id=2, FirstName="Mary", LastName="Blood" },
                new User {Id=1, FirstName="Sergey", LastName="Ivanov" }
            };

            var usersDistinct = users.Distinct().ToArray();
            Console.WriteLine(usersDistinct.Count()); //2 =John + Mary
Lapenkov Vladimir
  • 3,066
  • 5
  • 26
  • 37