web-dev-qa-db-ja.com

Linqの順番、グループの順番、各グループの順番?

次のようなオブジェクトがあります。

public class Student
{
    public string Name { get; set; } 
    public int Grade { get; set; }
}

次のクエリを作成します:学生名で成績をグループ化し、各学生グループを成績で順序付けし、各グループの最大成績でグループを順序付けします。

したがって、次のようになります。

A 100
A 80
B 80
B 50
B 40
C 70
C 30

次のクエリを作成しました。

StudentsGrades.GroupBy(student => student.Name)
    .OrderBy(studentGradesGroup => studentGradesGroup.Max(student => student.Grade));

しかし、それはIEnumerableIGroupingを返します。別のforeachクエリでリストをソートし、AddRangeを使用して結果を別のリストに追加しない限り、リストをソートする方法はありません。

それを行うよりきれいな方法はありますか?

60
Rita

承知しました:

var query = grades.GroupBy(student => student.Name)
                  .Select(group => 
                        new { Name = group.Key,
                              Students = group.OrderByDescending(x => x.Grade) })
                  .OrderBy(group => group.Students.First().Grade);

注文後、各グループ内で最初の成績を取得するだけで済むことに注意してください。これは、最初のエントリが最高の成績を持つことが既にわかっているためです。

次に、それらを表示できます:

foreach (var group in query)
{
    Console.WriteLine("Group: {0}", group.Name);
    foreach (var student in group.Students)
    {
        Console.WriteLine("  {0}", student.Grade);
    }
}
122
Jon Skeet

投影せずにそれを行う方法:

StudentsGrades.OrderBy(student => student.Name).
ThenBy(student => student.Grade);
19
Sawyer

各グループをグループのソートされたバージョンにマッピングする追加の投影が必要だと思います:

.Select(group => group.OrderByDescending(student => student.Grade))

また、あなたのように見えますmightその後に別の平坦化操作が必要です。これにより、グループのシーケンスではなく、学生のシーケンスが提供されます。

.SelectMany(group => group)

常に両方を折りたたみ、投影とフラット化を同時に行うsingleSelectMany呼び出しにできます。


編集:ジョン・スキートが指摘するように、全体的なクエリには特定の非効率性があります。各グループのソートから得られる情報は、グループ自体の順序付けには使用されていません。各グループのソートを移動してbeforeグループ自体の順序付けを行うことで、Maxクエリをより単純なFirstクエリに回避できます。

15
Ani

これを試して...

public class Student 
    {
        public int Grade { get; set; }
        public string Name { get; set; }
        public override string ToString()
        {
            return string.Format("Name{0} : Grade{1}", Name, Grade);
        }
    }

class Program
{
    static void Main(string[] args)
    {

      List<Student> listStudents = new List<Student>();
      listStudents.Add(new Student() { Grade = 10, Name = "Pedro" });
      listStudents.Add(new Student() { Grade = 10, Name = "Luana" });
      listStudents.Add(new Student() { Grade = 10, Name = "Maria" });
      listStudents.Add(new Student() { Grade = 11, Name = "Mario" });
      listStudents.Add(new Student() { Grade = 15, Name = "Mario" });
      listStudents.Add(new Student() { Grade = 10, Name = "Bruno" });
      listStudents.Add(new Student() { Grade = 10, Name = "Luana" });
      listStudents.Add(new Student() { Grade = 11, Name = "Luana" });
      listStudents.Add(new Student() { Grade = 22, Name = "Maria" });
      listStudents.Add(new Student() { Grade = 55, Name = "Bruno" });
      listStudents.Add(new Student() { Grade = 77, Name = "Maria" });
      listStudents.Add(new Student() { Grade = 66, Name = "Maria" });
      listStudents.Add(new Student() { Grade = 88, Name = "Bruno" });
      listStudents.Add(new Student() { Grade = 42, Name = "Pedro" });
      listStudents.Add(new Student() { Grade = 33, Name = "Bruno" });
      listStudents.Add(new Student() { Grade = 33, Name = "Luciana" });
      listStudents.Add(new Student() { Grade = 17, Name = "Maria" });
      listStudents.Add(new Student() { Grade = 25, Name = "Luana" });
      listStudents.Add(new Student() { Grade = 25, Name = "Pedro" });

      listStudents.GroupBy(g => g.Name).OrderBy(g => g.Key).SelectMany(g => g.OrderByDescending(x => x.Grade)).ToList().ForEach(x => Console.WriteLine(x.ToString()));
    }
}
4
Bruno Torres

または、次のようにすることもできます。

     var _items = from a in StudentsGrades
                  group a by a.Name;

     foreach (var _itemGroup in _items)
     {
        foreach (var _item in _itemGroup.OrderBy(a=>a.grade))
        {
           ------------------------
           --------------------------
        }
     }
1
agileDev