C# 如果某些属性匹配,则从列表中删除重复项,并根据某些顺序获取顶部项
我有一个对象列表,比如说用户C# 如果某些属性匹配,则从列表中删除重复项,并根据某些顺序获取顶部项,c#,linq,list,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/1
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
重要的一点是,它返回FilterId不为null的Josh记录。我觉得有点像一个带有orderby和firstordefault的Group By 我在编写包含多个字段的groupby子句并使其正常工作时遇到困难
修改示例以反映需要成为groupby一部分的其他属性。对多个属性进行分组的最简单方法是使用匿名类型:
var groups = users.GroupBy(user => new
{
Name,
Type,
SubType,
});
这是因为匿名类型的实现是这样的:它们重写Equals
方法来比较类型的每个成员的值,还重写GetHashCode
方法,使其成为对象所有值的散列的复合散列。如果您刚刚创建了一个新的命名类型,那么您需要自己处理所有这些,这非常繁琐。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是一个整数。它不能为null主要的理解是使用IEqualityComparer,类LambdaCustomComparer是使用LINQ动态执行此操作的帮助器 您需要复制并粘贴以下内容:
公共类用户
{
公共字符串名称{get;set;}
公共字符串Sex{get;set;}
公共日期时间生日{get;set;}
公共int?FilterId{get;set;}
}
公共类LambdaCustomComparer:IEqualityComparer
{
私有只读Func lambdaComparer;
私有只读Func lambdaHash;
公共LambdaCustomComparer(Func lambdaComparer,bool ignoreHashcode=true)
{
if(lambdaComparer==null)
抛出新ArgumentNullException(“lambdaComparer”);
this.lambdaComparer=lambdaComparer;
if(ignoreHashcode)
lambdaHash=obj=>0;
其他的
lambdaHash=EqualityComparer.Default.GetHashCode;
}
公共LambdaCustomComparer(Func lambdaComparer,Func lambdaHash)
{
if(lambdaComparer==null)
抛出新ArgumentNullException(“lambdaComparer”);
if(lambdaHash==null)
抛出新的ArgumentNullException(“lambdaHash”);
this.lambdaComparer=lambdaComparer;
this.lambdaHash=lambdaHash;
}
公共布尔等于(TX,TY)
{
返回lambdaComparer(x,y);
}
公共int GetHashCode(T obj)
{
返回lambdaHash(obj);
}
}
变量列表=新列表
{
新用户(){Name=“Josh”,Sex=“M”,生日=日期时间。现在,FilterId=null},
新用户(){Name=“John”,Sex=“M”,生日=日期时间。现在,FilterId=null},
新用户(){Name=“Jane”,Sex=“F”,生日=日期时间。现在,FilterId=null},
新用户(){Name=“Josh”,Sex=“M”,生日=日期时间。现在,FilterId=1},
新用户(){Name=“John”,Sex=“M”,生日=日期时间。现在,FilterId=null},
};
var comparer=new LambdaCustomComparer((a,b)=>a.Name==b.Name&&a.birth.Date==b.birth.Date&&a.Sex==b.Sex&&a.FilterId==b.FilterId);
var distinctList=list.GroupBy(user=>user,comparer).ToDictionary(a=>a.Key,b=>b.ToArray());
您的“FilterId”永远不能为空,因为它是一个int
。假设它实际上是一个int?
akaNullable
,这将起作用:
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()
;
IEnumerable users=GetSomeUsers();
用户[]distinctUsers=用户
.GroupBy(x=>新元组(x.Name,x.Sex,x.birth))
.SelectMany(g=>g.OrderBy(x=>x.FilterId.HasValue?0:1)
.ThenBy(x=>x.FilterId)
.采取(1)
)
.ToArray()
;
此代码
- 姓名、性别和出生日期分组
- 对筛选器id上的每个组进行排序,以便先整理非空值,然后整理空值
- 获取每个组的第一个元素(组中始终至少有一个元素)
- 将其展平为单个列表
- 将其转换为数组
.GroupBy(user=>user.Name)
。这就是全部。从这里开始,您需要使用给定的条件将每个组投影到单个项目中。@Servy我添加了额外的groupby列,以更准确地表示我的需要。我真的需要按名称、类型和子类型分组。这不起作用。您的分组依据将基于用户
对象的标识进行分组,而不是基于对象的值,除非您提供了自定义的IEqualityComparer
,定义该类型的值相等。@Servy修复并测试了它您更改了三种解决方案之一。两个仍然是错误的。这没有正确地对这些类型的值进行散列(它只是使用默认实现,而不是使用值语义),因此,GroupBy
不起作用。它不起作用,因为它将使用User
的默认GetHashCode
实现,它不会覆盖对象
的实现,该对象基于对象的引用创建哈希,而您的目标是基于该对象中的几个值创建哈希代码。我理解您的观点
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()
;