c# - 如何使用 C#/LINQ 计算加权平均值
问题描述
这是处理库存数据;数据格式如下:
public class A
{
public int Price;
public int Available;
}
让我们以这些数据为例:
var items = new List<A>
{
new A { Price = 10, Available = 1000 },
new A { Price = 15, Available = 500 },
new A { Price = 20, Available = 2000 },
};
我的查询返回特定数量的平均价格,例如:
如果我的请求量为 100,我的平均价格为 10
如果我的请求数量为 1200,我以 10 的价格购买前 1000 个,然后以 15 的价格购买接下来的 200 个,依此类推
我已经在 C# 中实现了这一点,但我试图找出是否可以使用 LINQ 直接使用数据库迭代器来完成。
我得到的数据已经按价格排序,但我不知道如何在没有迭代的情况下解决这个问题。
编辑:
这是代码:
public static double PriceAtVolume(IEnumerable<A> Data, long Volume)
{
var PriceSum = 0.0;
var VolumeSum = 0L;
foreach (var D in Data)
{
if (D.Volume < Volume)
{
PriceSum += D.Price * D.Volume;
VolumeSum += D.Volume;
Volume -= D.Volume;
}
else
{
PriceSum += D.Price * Volume;
VolumeSum += Volume;
Volume = 0;
}
if (Volume == 0) break;
}
return PriceSum / VolumeSum;
}
和测试代码:
var a = new List<A>
{
new A { Price = 10, Volume = 1000 },
new A { Price = 15, Volume = 500 },
new A { Price = 20, Volume = 2000 }
};
var P0 = PriceAtVolume(a, 100);
var P1 = PriceAtVolume(a, 1200);
澄清:
上面我说过我想将它移动到 LINQ 以使用数据库迭代器,所以我想避免扫描整个数据并在计算答案时停止迭代。数据已经在数据库中按价格排序。
解决方案
这可能是你能得到的最多的 Linqy。它使用接受三个参数的Aggregate
方法,特别是三个重载版本中最复杂的一个。Aggregate
第一个参数是种子,它被初始化为 zeroed ValueTuple<long, decimal>
。第二个参数是累加器函数,具有将种子和当前元素组合成新种子的逻辑。第三个参数采用最终的累积值并将它们投影到所需的平均值。
public static decimal PriceAtVolume(IEnumerable<A> data, long requestedVolume)
{
return data.Aggregate(
(Volume: 0L, Price: 0M), // Seed
(sum, item) => // Accumulator function
{
if (sum.Volume == requestedVolume)
return sum; // Goal reached, quick return
if (item.Available < requestedVolume - sum.Volume)
return // Consume all of it
(
sum.Volume + item.Available,
sum.Price + item.Price * item.Available
);
return // Consume part of it (and we are done)
(
requestedVolume,
sum.Price + item.Price * (requestedVolume - sum.Volume)
);
},
sum => sum.Volume == 0M ? 0M : sum.Price / sum.Volume // Result selector
);
}
更新:我将返回类型从双精度更改为小数,因为小数是货币值的首选类型。
顺便说一句,如果经常使用相同的数据调用此函数,并且数据列表很大,则可以通过将累积的摘要存储在 a 中来优化它List<(long, decimal)>
,并应用BinarySearch
以快速找到所需的条目。但是它变得复杂了,我不希望优化的先决条件会经常出现。
推荐阅读
- javascript - router.get() 到“正常”异步函数
- node.js - 附加保存在变量 NodeMailer 中的文件
- outlook - 事件更新 API 如何工作?(MS 图形 API)
- node.js - WebStorm + nvm + Typescript + ts-node + 断点调试 - 随机停止工作
- algorithm - 如何找到用户的朋友的数量(两条边的最短路径)?
- anylogic - 长时间运行后模型冻结并变得非常滞后
- javascript - 倒计时计时器在即将推出的 javascript 页面中
- firebase - 我应该捆绑哪些 Google Cloud 服务来为我的桌面应用程序添加在线存储支持?
- java - Webflux-Aop:在 Aspect 类中获取方法请求(单声道)
- r - 重复列中的值