c# - LINQ sum with grouping,还可以在分组前获取行项值
问题描述
我有一个类型的汽车列表,如下所示List<Car> cars
:
ID | 颜色 | 制作 | 模型 | 公里 |
---|---|---|---|---|
1 | 红色的 | 宝马 | M3 | 1000 |
2 | 红色的 | 宝马 | 318i | 1000 |
3 | 黑色的 | 梅赛德斯 | C200 | 1000 |
4 | 黑色的 | 梅赛德斯 | E200 | 1000 |
5 | 白色的 | 梅赛德斯 | CLA200 | 1000 |
6 | 白色的 | 梅赛德斯 | E200 | 1000 |
7 | 白色的 | 梅赛德斯 | E200 | 2000 |
8 | 白色的 | 梅赛德斯 | E200 | 3000 |
现在我想要做的是,使用 LINQ(或者如果不能使用 LINQ,则可能是另一种方式)我想按颜色、品牌、型号对它们进行分组,然后添加另一列并分配给另一种类型的列表(<CarDetail>
),这将给出组中线的加权平均值。
例如,在最后 3 辆汽车上,新列将显示(该组的总公里数 = 1000+2000+3000 = 6000)。
6 => 1000/6000
7 => 2000/6000
8 => 3000/6000
到目前为止我尝试过的是:
List<CarDetail> carDetails =
cars.
GroupBy(x => new
{
x.Colour,
x.Make,
x.Model
}).
Select(h => new CarDetail
{
Colour= h.Key.Colour,
Make= h.Key.Make,
Model= h.Key.Model,
WeightedKm = x.Km / h.Sum(x => x.Km )
}).ToList();
h.Sum(x => x.Km) 将带来 Km.s 的总和,但尝试使用 "x.Km" 肯定行不通。
任何人都可以帮助如何获取此列表吗?
解决方案
分组在概念上是列表的列表。似乎您想将其解压缩为一个简单的列表,沿途使用子列表的聚合。将列表列表转换为列表的过程是 SelectMany
var carDetails = cars
.GroupBy(c => new
{
c.Colour,
c.Make,
c.Model
})
.SelectMany(g => g, (subList, aCarInTheSublist) => new CarDetail
{
Colour= aCarInTheSublist.Colour,
Make= aCarInTheSublist.Make,
Model= aCarInTheSublist.Model,
WeightedKm = (double)aCarInTheSublist.Km / subList.Sum(aCar => aCar.Km ) //cast to double because int math 1000/3000 = 0
}).ToList();
用 Select 攻击它会遇到麻烦,因为您选择的是组,即外部列表。以这个更简单的例子为例:
A, 1
A, 2
A, 3
B, 1
如果按字母分组,则输出只有 2 行:
A -> {A,1} {A,2} {A,3}
B -> {B,1}
所以你很难回到你想要的 4 行,因为 Select 只运行了两次。
SelectMany 将访问组中的每个条目(即 A 组和 B 两个组,A 组有 3 个子条目)并访问每个子条目,因此对于 A 组(具有 3 个成员的单个组,A1, A2 和 A3) 它将导致 3 个元素向外突出。至关重要的是,虽然它访问了 A1、A2 和 A3 中的每一个,但仍然可以访问整个组 A,因此您可以将其中的所有 KM 相加
它可能效率不高,因为我们反复总结小组,但 LINQ 并不总能赢得效率竞赛 :)
您可以考虑通过将总和放入字典中来用 CPU 时间来换取内存以记住总和:
var d = cars
.GroupBy(c => new
{
c.Colour,
c.Make,
c.Model
})
.ToDictionary(g=>g.Key, g=>g.Sum(c=>c.Km));
var result = cars
.Select(c => new CarDetail
{
Colour= c.Colour,
Make= c.Make,
Model= c.Model,
WeightedKm = (double)c.Km / d[new{c.Colour,c.Make,c.Model}]
}).ToList();
旁注,当使用 LINQ 时,请尝试保留代表列表条目的别名。cars
是汽车的列表,所以Select(c =>
为了帮助保持直线,它是一辆车。
AGroupBy(c=>...).Select(g=>
提醒您正在对汽车进行分组,然后 Select 正在对分组进行操作。分组中的每个项目都是汽车,因此您可能喜欢Select(gc=>
“汽车组”,而Select(gc=> gc.Select(c=>
第二个选择是对汽车的可枚举进行操作,因此恢复到c
有助于澄清
推荐阅读
- mysql - 如何根据其他列的结果运行 SELECT 语句?
- reactjs - 如何在反应中使用onload?
- gitlab - gitlab推送规则,允许一个特定文件gradle-wrapper.jar
- android - Android 受限权限
- c# - 在 C# 中无法获取 PowerShell 命令的结果
- r - 将关键字与一系列文本评论匹配
- loops - 为什么我的计数器尽管增加却是负数?
- javascript - 循环遍历 JSON 以按键对值进行分组
- react-native - React Native 检测手机屏幕是否关闭
- sql-server - SQL Server 表在没有架构的情况下不显示 [Jetbrains Datagrip]