c# - 如何在 C# 中对具有原始顺序的列表进行排名

标签 c# linq list

我想从列表中进行排名并按原始顺序输出。

到目前为止,这是我的代码:

     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();

但是,结果将按降序排列:

ranking

我要的是按原来的顺序显示。

例如: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/

相关文章:

python - 仅将第一个和第三个元素从 python 列表打印到 csv 文件

c# - 在 c# winform 中更改的文本上启用禁用按钮

c# - EF 代码首先是 : one-to-many twice to same collection type

c# - Entity Framework : Why DbSet<T> Loads all data

c# - 如果类型不同(如 List<T> 和 List<G>),是否可以连接两个列表?

algorithm - OCaml 在列表中插入一个元素

c# - 打开文件只读

c# - 将 Gmail 日期转换为 Gmail 帐户中显示的日期时间

vb.net - LINQ 中的 Equals 和 = 有什么区别?

c# - 处理传递到 Controller 的模型中的空值