在 C# 中使用 LINQ 按范围排序列表

Posted

技术标签:

【中文标题】在 C# 中使用 LINQ 按范围排序列表【英文标题】:Sorting list by range using LINQ in C# 【发布时间】:2021-11-24 00:52:34 【问题描述】:

我有一个这样的列表:

            List<Student> students = new List<Student>
            
                new Student  Name = "M", Scores = new int[]  94, 92, 91, 91  ,
                new Student  Name = "I", Scores = new int[]  66, 87, 65, 93, 86 ,
                new Student  Name = "C", Scores = new int[]  76, 61, 73, 66, 54 ,
                new Student  Name = "D", Scores = new int[]  94, 55, 82, 62, 52 ,
                new Student  Name = "P", Scores = new int[]  91, 79, 58, 63, 55 ,
                new Student  Name = "E", Scores = new int[]  74, 85, 73, 75, 86 ,
                new Student  Name = "P", Scores = new int[]  73, 64, 53, 72, 68 ,
            

有什么方法可以计算出每个学生的平均分并按范围显示。 结果会是这样的:

Score > 90 and < 100
 M(student name) 92 (average score)
Score > 80 and < 90
 P 86.8
 I 83.4
 Y 82.4

我还需要计算有多少范围。例如,对于上述结果,我们有两个范围:(>90 和 80 和

我已经知道如何计算平均分数,但是我坚持将它们分组到范围中并仅使用 LINQ 计算范围数。

我想学习怎么做。

【问题讨论】:

【参考方案1】:

您可以结合使用 LINQ 的 AverageSelectGroupBy,以及一些算术:

var result = string.Join("\r\n",
    students.Select(s =>
        (s.Name, Average: s.Scores.Average(sc => (double)sc)))
    .GroupBy(s => (int)Math.Ceiling(s.Average / 10))
    .OrderByDescending(g => g.Key)
    .Select(g =>
        $"Score >= g.Key * 10 - 10 and < g.Key * 10\r\n"
        + string.Join("\r\n", g.Select(s => $" s.Name s.Average:F1"))
    );

或略有不同

var result = string.Join("\r\n",
    students.Select(s =>
        (s.Name, Average: s.Scores.Average(sc => (double)sc)))
    .GroupBy(s => (int)Math.Ceiling(s.Average / 10))
    .OrderByDescending(g => g.Key)
    .SelectMany(g =>
        g.Select(s => $" s.Name s.Average:F1")
         .Prepend($"Score >= g.Key * 10 - 10 and < g.Key * 10\r\n"))
    );

【讨论】:

我尝试导入代码,Average出现错误,错误为“当前上下文中不存在Average的名称”。 抱歉现在应该修复 非常感谢。运行后,建议你将 $"Score >= g.Key * 10 和 = g.Key * 10 - 10 和 【参考方案2】:

首先,请注意,当平均分正好是 90 分时,您将需要一个案例。我假设这将由较高的存储桶处理,但如果您需要较低的存储桶,您可以更改逻辑桶。

最好按分数的“等级字母”计算和分组,因为它是一个字母,字母很容易按字母顺序排列。

var studentsByGrade = students
    .Select(x => new 
        x.Name,
        AvgScore = x.Scores.Average()
    )
    .GroupBy(x => GetGradeLetter(x.AvgScore));

这将使用辅助方法。

private static string GetGradeLetter(double score)

    if (score is >= 90)
        return "A";
    
    if (score is >= 80)
        return "B";

    // add more as you'd like
    
    return "ZZZ";

值得注意的是,您不需要在此处显示该字母 - 它只是用于方便订购,而且很可能这就是您最终会使用的。通常,您会将低于“60”的任何内容标记为一组,因为(至少在美国学校系统中)这意味着“F”。

要显示结果,请使用两个 foreaches。

foreach (var grade in studentsByGrade.OrderBy(x => x.Key))

    foreach (var student in grade.OrderByDescending(x => x.AvgScore))
    
        Console.WriteLine($"student.Name student.AvgScore");
    
    Console.WriteLine();

【讨论】:

以上是关于在 C# 中使用 LINQ 按范围排序列表的主要内容,如果未能解决你的问题,请参考以下文章

使用 Linq 按名称频率对列表进行排序

使用 C# 、 LINQ 进行排序

如何在 Linq C# 中使用嵌套字典对列表进行排序?

Linq 首先按特定数字排序,然后按顺序显示所有其余部分

使用动态 LINQ 按一个或多个属性对 JSON 进行排序

c# LINQ-to-XML 更新(保存)排序后文件中的元素列表