C# LINQ - 按属性分组列表,然后按不同组选择
Posted
技术标签:
【中文标题】C# LINQ - 按属性分组列表,然后按不同组选择【英文标题】:C# LINQ - Group List by a property then select by different groups 【发布时间】:2021-09-06 07:46:31 【问题描述】:我有一个要按属性A
分组的列表。然后选择GroupBy
键为null
或''
的该组的所有元素,然后我想返回这些组的所有元素。否则,对于组键,返回该组的第一个元素。我想出了以下内容以及我对代码的期望。任何帮助将不胜感激。
示例代码:
public class ABC
public string A get; set;
public string B get; set;
public string C get; set;
var list = new List<ABC>()
new ABC() A = "a", B = "b", C = "c" ,
new ABC() A = "a", B = "b", C = "c" ,
new ABC() A = null, B = "b", C = "c" ,
new ABC() A = null, B = "b", C = "c" ,
new ABC() A = "", B = "b", C = "c" ,
new ABC() A = "", B = "b", C = "c" ,
;
var result = list.GroupBy(x => x.A)
.Select(x =>
if (!string.IsNullOrEmpty(x.Key))
return x.First();
else
return x;
)
.ToList();
我的期望:
我希望从结果中看到5
的总数
A = "a", B = "b", C = "c" count 1 (1 removed)
A = null, B = "b", C = "c" count 2 (none removed)
A = "", B = "b", C = "c" count 2 (none removed)
【问题讨论】:
在您的Select
中,您可以将其修改为返回列表(其中某些列表仅包含一项),例如:var result = list.GroupBy(x => x.A).Select(x => string.IsNullOrEmpty(x.Key) ? x.ToList() : new List<ABC> x.First() ).ToList();
【参考方案1】:
类似这样的:
var result = list
.GroupBy(item => item.A)
.SelectMany(group => string.IsNullOrEmpty(group.Key)
? group as IEnumerable<ABC>
: new ABC[] group.First())
.ToList();
这里的诀窍是我们应该返回collection:要么是整个组,要么是一个仅包含First
项的集合(数组)。
您可以摆脱 grouping 和 flatten 以获得更快的版本,但是会利用 副作用:
HashSet<string> uniqueA = new HashSet<string>();
var result = list
.Where(item => string.IsNullOrEmpty(item.A) || uniqueA.Add(item.A))
.ToList();
【讨论】:
【参考方案2】:var result = list.GroupBy(x => x.A)
.Select(g =>
new
Key = g.Key,
Values = string.IsNullOrEmpty(g.Key) ? g.ToList() : new List<ABC> g.FirstOrDefault()
)
.ToList();
【讨论】:
【参考方案3】:你必须使用 LINQ 吗? @pwilcox 的答案很好,但您可能会发现这样的内容更具可读性:
var newList = new List<ABC>();
var setOfA = new HashSet<string>();
foreach (var abc in list)
if (string.IsNullOrEmpty(abc.A))
newList.Add(abc);
else
if (setOfA.Contains(abc.A))
continue;
setOfA.Add(abc.A);
newList.Add(abc);
【讨论】:
【参考方案4】:几点建议:
可选,但考虑使用三元运算符而不是 if/else。 如果IsNullOrEmpty
为假,则不要使用First
,而是使用Where
并过滤“0”的索引,以便它保持列表形式,即使其中只有一项。
使用SelectMany
而不是Select
来展平结果
以下是实际操作中的指针:
var result =
list
.GroupBy(x => x.A)
.SelectMany(x =>
string.IsNullOrEmpty(x.Key)
? x
: x.Where((val,ix) => ix == 0)
)
.ToList();
使用您的示例数据生成的 LINQPad 转储:
A | B | C |
---|---|---|
a | b | c |
null | b | c |
null | b | c |
b | c | |
b | c |
【讨论】:
以上是关于C# LINQ - 按属性分组列表,然后按不同组选择的主要内容,如果未能解决你的问题,请参考以下文章
C# LINQ NET 3.5 SP1:使用 LINQ 按两个字段分组,并为组的所有成员分配一个相关的唯一 ID(整数)