我想从列表中进行排名并按原始顺序输出。
到目前为止,这是我的代码:
var data = new[] { 7.806468478, 7.806468478, 7.806468478, 7.173501754, 7.173501754, 7.173501754, 3.40877696, 3.40877696, 3.40877696,
4.097010736, 4.097010736, 4.097010736, 4.036494085, 4.036494085, 4.036494085, 38.94333318, 38.94333318, 38.94333318, 14.43588131, 14.43588131, 14.43588131 };
var rankings = data.OrderByDescending(x => x)
.GroupBy(x => x)
.SelectMany((g, i) =>
g.Select(e => new { Col1 = e, Rank = i + 1 }))
.ToList();
但是,结果将按降序排列:
我要的是按原来的顺序显示。
例如:Rank = 3,Rank = 3,Rank = 3,Rank = 4,Rank = 4,Rank = 4,等等......
谢谢。
最佳答案
使用现有的方法,一种方法是跟踪原始顺序并再次排序(丑陋且可能很慢):
var rankings = data.Select((x, i) => new {Item = x, Index = i})
.OrderByDescending(x => x.Item)
.GroupBy(x => x.Item)
.SelectMany((g, i) =>
g.Select(e => new {
Index = e.Index,
Item = new { Col1 = e.Item, Rank = i + 1 }
}))
.OrderBy(x => x.Index)
.Select(x => x.Item)
.ToList();
我建议用您的排名创建一个字典,然后将其加入您的列表:
var rankings = data.Distinct()
.OrderByDescending(x => x)
.Select((g, i) => new { Key = g, Rank = i + 1 })
.ToDictionary(x => x.Key, x => x.Rank);
var output = data.Select(x => new { Col1 = x, Rank = rankings[x] })
.ToList();
正如@AntonínLejsek 所指出的那样,将上面的 GroupBy
调用替换为 Distinct()
是可行的方法。
注意 double
不是精确类型,因此确实不是查找表中值的良好候选者,我也不建议使用 GroupBy
/ Distinct
以浮点值作为键。请注意您的精度并考虑使用适当的字符串转换。鉴于此,您可能想要定义一个 epsilon 值并完全放弃 LINQ 的 GroupBy
,而是选择将每个数据点封装到一个(非匿名)引用类型中,然后遍历一个排序列表并分配等级。例如(免责声明:未经测试):
class DataPoint
{
decimal Value { get; set; }
int Rank { get; set; }
}
var dataPointsPreservingOrder = data.Select(x => new DataPoint {Value = x}).ToList();
var sortedDescending = dataPointsPreservingOrder.OrderByDescending(x => x.Value).ToList();
var epsilon = 1E-15; //use a value that makes sense here
int rank = 0;
double? currentValue = null;
foreach(var x in sortedDescending)
{
if(currentValue == null || Math.Abs(x.Value - currentValue.Value) > epsilon)
{
currentValue = x.Value;
++rank;
}
x.Rank = rank;
}
关于c# - 如何在 C# 中对具有原始顺序的列表进行排名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41582542/