c# - 如何使用 LINQ 按值范围对项目进行分组
问题描述
我在使用 linq 对对象进行分组时遇到问题,想知道是否有人可以告诉我我在这里做错了什么。此外,选择功能是我从此处(链接)获取的扩展,因此我可以比较前一个值和当前值,如果值在一个范围之间,那么我将值设置为当前值。
// Range of values, the first item in the group data, i
var ranges = new List<double> { 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0 };
// Simple class i created
public class CurrencyGroupItemData
{
public string Code { get; set; }
public double TotalStrength { get; set; }
}
var lstCurrencyGroups = new List<CurrencyGroupItemData>();
lstCurrencyGroups.Add(new CurrencyGroupItemData
{ Code = "USD", TotalStrength = 5.0 });
lstCurrencyGroups.Add(new CurrencyGroupItemData
{ Code = "CHF",TotalStrength = 2.14285714285714 });
lstCurrencyGroups.Add(new CurrencyGroupItemData
{ Code = "EUR",TotalStrength = 3.85714285714286 });
lstCurrencyGroups.Add(new CurrencyGroupItemData
{ Code = "GBP",TotalStrength = 3.42857142857143 });
lstCurrencyGroups.Add(new CurrencyGroupItemData
{ Code = "JPY",TotalStrength = 5.71428571428571 });
lstCurrencyGroups.Add(new CurrencyGroupItemData
{ Code = "CAD",TotalStrength = 6.85714285714286 });
lstCurrencyGroups.Add(new CurrencyGroupItemData
{ Code = "AUD",TotalStrength = 4.28571428571429 });
lstCurrencyGroups.Add(new CurrencyGroupItemData
{ Code = "NZD",TotalStrength = 4.71428571428571 });
// compare The total strength of each object, if it's value is between any of ranges above then group item by the range value. for example if the object totalstrength value is 5.7 that value is between 6.0 and 5.0, it's range value would the minimum of the 2 which 5.0, i would group that item by 5.0.
var jjjj01 = lstCurrencyGroups.GroupBy(x => ranges.SelectWithPrev((double r1, double r2, bool isfirst)
=> (isfirst && x.TotalStrength >= r1) ? r1 : (x.TotalStrength <= r1 && x.TotalStrength <= r2) ? r2 : 0.0).ToArray())
.Select(g => new { Rank = g.Key, Count = g.Count() })
.ToList();
// the extension i grabbed from the link above
public static IEnumerable<TResult> SelectWithPrev<TSource, TResult>
(this IEnumerable<TSource> source, Func<TSource, TSource, bool, TResult> projection)
{
using (var iterator = source.GetEnumerator())
{
var isfirst = true;
var previous = default(TSource);
while (iterator.MoveNext())
{
yield return projection(iterator.Current, previous, isfirst);
isfirst = false;
previous = iterator.Current;
}
}
}
解决方案
使用我自己的扩展来配对范围:
public static IEnumerable<TResult> ScanByPairs<T, TResult>(this IEnumerable<T> src, Func<T, T, TResult> combineFn) {
using (var srce = src.GetEnumerator()) {
if (srce.MoveNext()) {
var prev = srce.Current;
while (srce.MoveNext()) {
yield return combineFn(prev, srce.Current);
prev = srce.Current;
}
}
}
}
答案很简单。将范围转换为范围对:
var rangesWithKey = ranges.ScanByPairs((p,c) => (max: p, min: c)).ToList();
然后按包含TotalStrength
.
var ans = lstCurrencyGroups.GroupBy(cg => rangesWithKey.FirstOrDefault(rk => rk.min <= cg.TotalStrength && cg.TotalStrength <= rk.max).min)
.Select(cgg => new { Rank = cgg.Key, Count = cgg.Count() })
.OrderBy(cgg => cgg.Rank);
推荐阅读
- javascript - 使 setTimeout 无限
- javascript - 如何通过单击单元格更改颜色
- javascript - 如何清理 useEffect 函数?
- r - R while 在一天中的特定时间循环
- c - Mindstorms 的robotc **错误**:未定义的变量“sonarSensor”。假设为“短”
- sql - 不支持数据类型间隔 - Spark SQL
- javascript - 使用 JavaScript 动态生成 HTML 元素列表的最佳方法是什么?
- javascript - 在没有输入字段的情况下使用新复选框刷新 javascript 计算的总和
- c++ - MSVC2019 静态库中缺少符号名称
- android - 如何使用 ejabberd 聊天应用程序通过 API 层传递服务器