通过id去除对象列表中的重复项

10

我有一个程序,其中有一个主题(类似于论坛),人们可以对该主题做出反应。

用户:

  1. id
  2. 名字
  3. 姓氏

主题:

  1. id
  2. 主题

反应:

  1. id
  2. 主题id
  3. 内容

代码:

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

现在,“distinct”列表仍然有一些重复项,如何基于用户ID对列表进行不同处理?或者也许有另一种更好的方法来做到这一点。先感谢你。

5个回答

28
你可以使用 GroupBy 来实现这一点。
var ListOfUsers = ListOfAllUsers.GroupBy(x => x.Id)
                                  .Select(g => g.First())
                                  .ToList();

2
+1 同意,解决方案有效。它不应该被踩(尤其是没有任何评论的情况下)。 - Sergey Berezovskiy
@NiekJonkman 请注意,您将从数据库中下载所有重复用户数据,并在客户端过滤出重复用户。此外,我建议您在查询中包含相关的用户实体,以避免为每个用户进行额外的延迟查询。 - Sergey Berezovskiy
我知道这一点,但是我必须执行两个查询(我在原始帖子中只描述了一个,因为我只需要知道它的工作原理),而且我不知道如果您使用了两个不同的去重查询,是否可以填充列表并对该列表进行去重。 - Niek Jonkman
@NiekJonkman 在这种情况下,分组当然是解决方案。顺便说一句,如果您为您的“User”实体定义了“Equals”和“GetHashCode”方法,您将能够简单地调用“Distinct()”方法从列表中获取唯一的用户。 - Sergey Berezovskiy

4

Distinct有一个重载方法,接受一个IEqualityComparer<T>的实例作为参数,该对象包含逻辑,使得LINQ知道哪两个对象是相等的,从而可以消除其中一个。

你需要实现这个(非常简单的)接口,类似于下面这样:

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

然后将UserEqualityComparer的实例传递给Distinct()

var ListOfUsers = ListOfAllUsers.Distinct(new UserEqualityComparer()).ToList();

与 Linq 解决方案相比,有哪些优缺点? - Niek Jonkman

3

我建议您让数据库为您返回不同的用户:

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

那将被翻译成单个的SQL查询语句。类似于以下内容(假设您的USER表有两列 - Id和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]

这不是只有在模型/用户类已经正确实现了“Equals”方法的情况下才能起作用吗?否则,“Distinct”本身无法针对模型列表工作。因此才会有这个问题。 - Don Cheadle

2
MoreLinq(可在NuGet上获得)有一个DistincBy方法,允许您使用委托作为相等比较器。

因此,您只需要像这样做:

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

编辑:

MoreLinq链接


您可以在此处找到DistinctBy扩展方法的代码:http://stackoverflow.com/a/20397508/1714342 - Kamil Budziewski

0

我尝试在 .Net Core 中使用类似的代码。不需要定义明确的比较器。 在类中覆盖 EqualsGetHashCode。 不需要使用 GroupBy。默认情况下,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

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