如果某些属性匹配,则从列表中删除重复项,并根据某些排序获取最高项

时间:2013-09-13 18:08:22

标签: c# linq list

我有一个对象列表让我们说用户。

public class User
{
public string Name {get;set;}
public string Sex {get;set;}
public DateTime Birthday {get;set;}
public string Type {get;set;}
public string SubType {get;set;}
public int FilterId {get;set;}
}

假设我有以下结果

Josh, M, 5/1/1980, Admin, Main, null
John, M, 5/1/1980, User, Main, null
Jane, F, 5/2/1980, User, Main, null
Josh, M, 5/1/1980, Admin, Main 1
John, M, 5/1/1980, User, Main, null
Josh, M, 5/1/1980, User, Main, null

我想得到以下结果。

Josh, M, 5/1/1980, Admin, Main 1
John, M, 5/1/1980, User, Main, null
Jane, F, 5/2/1980, User, Main, null
Josh, M, 5/1/1980, User, Main, null

重要的是它返回了Josh的记录,其中FilterId不为null。我认为类似于Group By with Order By和firstordefault。

我在编写包含多个字段的groupby子句时遇到问题,并使其正常工作。

修改示例以反映需要成为groupby一部分的其他属性。

4 个答案:

答案 0 :(得分:3)

您的&#39; FilterId&#39;永远不能为空,因为它是int。假设它实际上是int?又称Nullable<int>,这将起作用:

IEnumerable<User> users = GetSomeUsers() ;
User[] distinctUsers = users
                       .GroupBy( x => new Tuple<string,string,DateTime>(x.Name,x.Sex,x.Birthday))
                       .SelectMany( g => g.OrderBy( x=> x.FilterId.HasValue ? 0 : 1 )
                                          .ThenBy( x => x.FilterId )
                                          .Take(1)
                       )
                       .ToArray()
                            ;

此代码

  • 名称,性别和生日的小组
  • 对过滤器ID上的每个组进行排序,使得非空值首先进行整理,然后进行空值
  • 获取每个组的第一个元素(组中始终至少有一个元素)。
  • 将其展平为单个列表
  • 将其变成数组。

答案 1 :(得分:1)

groupby部分:

.GroupBy(x => new { x.Name , x.Sex, x.Birthday});

从这里开始选择第一个:

.GroupBy(x => new { x.Name , x.Sex, x.Birthday}).Select(y=>x.First());

如果您希望第一个没有null,则可以执行OrderByDescending

.OrderByDescending(usr=>usr.FilterId ).GroupBy(x => new { x.Name , x.Sex, x.Birthday}).Select(y=>y.First()).ToList();

filterId是一个整数。它不能为空

答案 2 :(得分:0)

对多个属性进行分组的最简单方法是使用匿名类型:

var groups = users.GroupBy(user => new
    {
        Name,
        Type,
        SubType,
    });

这是有效的,因为匿名类型的实现使得它们覆盖Equals方法以比较该类型的每个成员的值,并且还覆盖GetHashCode方法以使其成为复合哈希对象的所有值的哈希值。如果你刚刚创建了一个新的命名类型,你需要自己处理所有这些,这简直是单调乏味的。

答案 3 :(得分:-1)

主要的理解是使用IEqualityComparer,类LambdaCustomComparer是一个帮助器,可以动态地使用LINQ来完成。

您需要复制并粘贴此内容:


public class User
{
    public string Name { get; set; }
    public string Sex { get; set; }
    public DateTime Birthday { get; set; }
    public int? FilterId { get; set; }
}

public class LambdaCustomComparer<T> : IEqualityComparer<T>
{
    private readonly Func<T, T, bool> lambdaComparer;
    private readonly Func<T, int> lambdaHash;

    public LambdaCustomComparer(Func<T, T, bool> lambdaComparer, bool ignoreHashcode = true)
    {
        if (lambdaComparer == null)
            throw new ArgumentNullException("lambdaComparer");

        this.lambdaComparer = lambdaComparer;
        if (ignoreHashcode)
            lambdaHash = obj => 0;
        else
            lambdaHash = EqualityComparer<T>.Default.GetHashCode;
    }

    public LambdaCustomComparer(Func<T, T, bool> lambdaComparer, Func<T, int> lambdaHash)
    {
        if (lambdaComparer == null)
            throw new ArgumentNullException("lambdaComparer");
        if (lambdaHash == null)
            throw new ArgumentNullException("lambdaHash");

        this.lambdaComparer = lambdaComparer;
        this.lambdaHash = lambdaHash;
    }

    public bool Equals(T x, T y)
    {
        return lambdaComparer(x, y);
    }

    public int GetHashCode(T obj)
    {
        return lambdaHash(obj);
    }
}

var list = new List<User>
{
    new User() {Name = "Josh", Sex= "M", Birthday = DateTime.Now, FilterId = null},
    new User() {Name = "John", Sex= "M", Birthday = DateTime.Now, FilterId = null},
    new User() {Name = "Jane", Sex= "F", Birthday = DateTime.Now, FilterId = null},
    new User() {Name = "Josh", Sex= "M", Birthday = DateTime.Now, FilterId = 1},
    new User() {Name = "John", Sex= "M", Birthday = DateTime.Now, FilterId = null},
};

var comparer = new LambdaCustomComparer<User>((a, b) => a.Name == b.Name && a.Birthday.Date == b.Birthday.Date && a.Sex == b.Sex && a.FilterId == b.FilterId);

var distinctList = list.GroupBy(user => user, comparer).ToDictionary(a => a.Key, b => b.ToArray());