[C#/LINQ] ThenByメソッドの使い方 – ソート済みシーケンスを昇順ソートする

ThenByメソッドは、C#のLINQクエリで既にソートされたシーケンスに対して、追加の昇順ソート条件を指定するために使用されます。

通常、OrderByメソッドで最初のソート条件を指定し、その後にThenByを使ってさらに細かいソートを行います。

例えば、最初に名前でソートし、次に年齢でソートする場合に使用します。

降順でソートしたい場合は、ThenByDescendingメソッドを使用します。

この記事でわかること
  • ThenByメソッドの基本的な使い方
  • 複数の条件でのソート方法
  • ソートのパフォーマンスに関する注意点
  • null値や重複条件の扱い
  • ソート結果の安定性についての理解

目次から探す

ThenByメソッドとは

ThenByメソッドは、C#のLINQ(Language Integrated Query)において、既にソートされたシーケンスに対して追加のソートを行うためのメソッドです。

このメソッドは、OrderByメソッドで最初のソートを行った後に、さらに別のプロパティに基づいて昇順でソートを行う際に使用されます。

ThenByメソッドを使うことで、複数の条件に基づいたソートが可能になり、データの整列をより柔軟に行うことができます。

例えば、名前でソートした後に年齢でソートする場合など、複数の基準を組み合わせてデータを整理することができます。

これにより、データの可読性が向上し、特定の条件に基づいた情報の抽出が容易になります。

ThenByメソッドの基本的な使い方

シンプルな例:単一のプロパティでのソート

ThenByメソッドを使ったシンプルな例として、整数のリストを昇順にソートする方法を示します。

まず、OrderByメソッドで最初のソートを行い、その後ThenByメソッドで追加のソートを行います。

以下のコードでは、数値のリストを昇順にソートしています。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 5, 3, 8, 1, 4 };
        
        var sortedNumbers = numbers.OrderBy(n => n) // 最初のソート
                                   .ThenBy(n => n); // ThenByで追加のソート
        
        foreach (var number in sortedNumbers)
        {
            Console.WriteLine(number);
        }
    }
}
1
3
4
5
8

複数のプロパティでのソート

複数のプロパティを使ったソートの例として、オブジェクトのリストを年齢と名前でソートする方法を示します。

まず、OrderByメソッドで年齢を基準にソートし、その後ThenByメソッドで名前を基準にソートします。

using System;
using System.Collections.Generic;
using System.Linq;
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
class Program
{
    static void Main()
    {
        List<Person> people = new List<Person>
        {
            new Person { Name = "田中", Age = 25 },
            new Person { Name = "佐藤", Age = 20 },
            new Person { Name = "鈴木", Age = 25 },
            new Person { Name = "山田", Age = 30 }
        };
        
        var sortedPeople = people.OrderBy(p => p.Age) // 年齢でソート
                                 .ThenBy(p => p.Name); // 名前で追加ソート
        
        foreach (var person in sortedPeople)
        {
            Console.WriteLine($"{person.Name}, {person.Age}歳");
        }
    }
}
佐藤, 20歳
田中, 25歳
鈴木, 25歳
山田, 30歳

匿名型を使ったソート

匿名型を使ったソートの例では、複数のプロパティを持つオブジェクトを匿名型でまとめてソートします。

以下のコードでは、名前と年齢を持つ匿名型のリストを作成し、年齢でソートした後に名前でソートします。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        var people = new[]
        {
            new { Name = "田中", Age = 25 },
            new { Name = "佐藤", Age = 20 },
            new { Name = "鈴木", Age = 25 },
            new { Name = "山田", Age = 30 }
        };
        
        var sortedPeople = people.OrderBy(p => p.Age) // 年齢でソート
                                 .ThenBy(p => p.Name); // 名前で追加ソート
        
        foreach (var person in sortedPeople)
        {
            Console.WriteLine($"{person.Name}, {person.Age}歳");
        }
    }
}
佐藤, 20歳
田中, 25歳
鈴木, 25歳
山田, 30歳

カスタムコンパレータを使ったソート

カスタムコンパレータを使ったソートでは、独自のロジックを定義してソートを行います。

以下の例では、年齢を基準にソートし、同じ年齢の場合は名前の長さでソートします。

using System;
using System.Collections.Generic;
using System.Linq;
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
class Program
{
    static void Main()
    {
        List<Person> people = new List<Person>
        {
            new Person { Name = "田中", Age = 25 },
            new Person { Name = "佐藤", Age = 20 },
            new Person { Name = "鈴木", Age = 25 },
            new Person { Name = "山田", Age = 30 }
        };
        
        var sortedPeople = people.OrderBy(p => p.Age) // 年齢でソート
                                 .ThenBy(p => p.Name.Length); // 名前の長さで追加ソート
        
        foreach (var person in sortedPeople)
        {
            Console.WriteLine($"{person.Name}, {person.Age}歳");
        }
    }
}
佐藤, 20歳
田中, 25歳
鈴木, 25歳
山田, 30歳

これらの例を通じて、ThenByメソッドの基本的な使い方を理解することができます。

複数の条件でのソートが可能であり、データの整理がより柔軟に行えることがわかります。

ThenByメソッドの実践例

文字列のリストをソートする

文字列のリストをThenByメソッドを使ってソートする例を示します。

最初に文字列のリストを長さでソートし、同じ長さの文字列がある場合はアルファベット順でソートします。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<string> words = new List<string> { "apple", "banana", "kiwi", "grape", "fig", "pear" };
        
        var sortedWords = words.OrderBy(w => w.Length) // 文字列の長さでソート
                               .ThenBy(w => w); // アルファベット順で追加ソート
        
        foreach (var word in sortedWords)
        {
            Console.WriteLine(word);
        }
    }
}
fig
kiwi
pear
apple
grape
banana

数値のリストをソートする

数値のリストをThenByメソッドを使ってソートする例です。

最初に偶数と奇数で分け、次にそれぞれのグループ内で昇順にソートします。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 5, 3, 8, 1, 4, 2, 7, 6 };
        
        var sortedNumbers = numbers.OrderBy(n => n % 2) // 偶数と奇数で分ける
                                   .ThenBy(n => n); // 昇順で追加ソート
        
        foreach (var number in sortedNumbers)
        {
            Console.WriteLine(number);
        }
    }
}
2
4
6
8
1
3
5
7

オブジェクトのリストをソートする

オブジェクトのリストをThenByメソッドを使ってソートする例です。

ここでは、学生のリストを年齢でソートし、同じ年齢の学生がいる場合は名前でソートします。

using System;
using System.Collections.Generic;
using System.Linq;
class Student
{
    public string Name { get; set; }
    public int Age { get; set; }
}
class Program
{
    static void Main()
    {
        List<Student> students = new List<Student>
        {
            new Student { Name = "田中", Age = 20 },
            new Student { Name = "佐藤", Age = 22 },
            new Student { Name = "鈴木", Age = 20 },
            new Student { Name = "山田", Age = 21 }
        };
        
        var sortedStudents = students.OrderBy(s => s.Age) // 年齢でソート
                                     .ThenBy(s => s.Name); // 名前で追加ソート
        
        foreach (var student in sortedStudents)
        {
            Console.WriteLine($"{student.Name}, {student.Age}歳");
        }
    }
}
田中, 20歳
鈴木, 20歳
山田, 21歳
佐藤, 22歳

複数の条件でのソート

複数の条件でのソートの例として、製品のリストを価格でソートし、同じ価格の製品がある場合は名前でソートします。

using System;
using System.Collections.Generic;
using System.Linq;
class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}
class Program
{
    static void Main()
    {
        List<Product> products = new List<Product>
        {
            new Product { Name = "リンゴ", Price = 100 },
            new Product { Name = "バナナ", Price = 50 },
            new Product { Name = "オレンジ", Price = 100 },
            new Product { Name = "グレープ", Price = 80 }
        };
        
        var sortedProducts = products.OrderBy(p => p.Price) // 価格でソート
                                     .ThenBy(p => p.Name); // 名前で追加ソート
        
        foreach (var product in sortedProducts)
        {
            Console.WriteLine($"{product.Name}, {product.Price}円");
        }
    }
}
バナナ, 50円
グレープ, 80円
リンゴ, 100円
オレンジ, 100円

これらの実践例を通じて、ThenByメソッドを使ったさまざまなソートの方法を理解することができます。

複数の条件を組み合わせてデータを整理することで、より効果的な情報の抽出が可能になります。

ThenByメソッドの応用

複雑なオブジェクトのソート

複雑なオブジェクトのリストをThenByメソッドを使ってソートする例を示します。

ここでは、従業員のリストを部門でソートし、同じ部門内で年齢でソートします。

using System;
using System.Collections.Generic;
using System.Linq;
class Employee
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Department { get; set; }
}
class Program
{
    static void Main()
    {
        List<Employee> employees = new List<Employee>
        {
            new Employee { Name = "田中", Age = 30, Department = "営業" },
            new Employee { Name = "佐藤", Age = 25, Department = "開発" },
            new Employee { Name = "鈴木", Age = 35, Department = "営業" },
            new Employee { Name = "山田", Age = 28, Department = "開発" }
        };
        
        var sortedEmployees = employees.OrderBy(e => e.Department) // 部門でソート
                                       .ThenBy(e => e.Age); // 年齢で追加ソート
        
        foreach (var employee in sortedEmployees)
        {
            Console.WriteLine($"{employee.Name}, {employee.Age}歳, {employee.Department}");
        }
    }
}
田中, 30歳, 営業
鈴木, 35歳, 営業
佐藤, 25歳, 開発
山田, 28歳, 開発

ソート順を動的に変更する

ソート順を動的に変更する方法として、ユーザーの入力に基づいてソート条件を変更する例を示します。

以下のコードでは、年齢または名前でソートするかを選択できます。

using System;
using System.Collections.Generic;
using System.Linq;
class Student
{
    public string Name { get; set; }
    public int Age { get; set; }
}
class Program
{
    static void Main()
    {
        List<Student> students = new List<Student>
        {
            new Student { Name = "田中", Age = 20 },
            new Student { Name = "佐藤", Age = 22 },
            new Student { Name = "鈴木", Age = 20 },
            new Student { Name = "山田", Age = 21 }
        };
        
        Console.WriteLine("ソート条件を選択してください (1: 年齢, 2: 名前): ");
        var choice = Console.ReadLine();
        
        var sortedStudents = choice == "1" 
            ? students.OrderBy(s => s.Age) // 年齢でソート
            : students.OrderBy(s => s.Name); // 名前でソート
        
        foreach (var student in sortedStudents)
        {
            Console.WriteLine($"{student.Name}, {student.Age}歳");
        }
    }
}

出力結果(年齢でソートを選択した場合):

田中, 20歳
鈴木, 20歳
山田, 21歳
佐藤, 22歳

ソート結果を逆順にする

ThenByメソッドを使ってソート結果を逆順にする方法を示します。

以下の例では、年齢でソートした後、ThenByDescendingメソッドを使って名前を逆順でソートします。

using System;
using System.Collections.Generic;
using System.Linq;
class Student
{
    public string Name { get; set; }
    public int Age { get; set; }
}
class Program
{
    static void Main()
    {
        List<Student> students = new List<Student>
        {
            new Student { Name = "田中", Age = 20 },
            new Student { Name = "佐藤", Age = 22 },
            new Student { Name = "鈴木", Age = 20 },
            new Student { Name = "山田", Age = 21 }
        };
        
        var sortedStudents = students.OrderBy(s => s.Age) // 年齢でソート
                                     .ThenByDescending(s => s.Name); // 名前を逆順で追加ソート
        
        foreach (var student in sortedStudents)
        {
            Console.WriteLine($"{student.Name}, {student.Age}歳");
        }
    }
}
田中, 20歳
鈴木, 20歳
山田, 21歳
佐藤, 22歳

ソート結果をフィルタリングする

ソート結果をフィルタリングする方法として、特定の条件に基づいてデータを絞り込む例を示します。

以下のコードでは、年齢が20歳以上の学生を年齢でソートし、同じ年齢の学生がいる場合は名前でソートします。

using System;
using System.Collections.Generic;
using System.Linq;
class Student
{
    public string Name { get; set; }
    public int Age { get; set; }
}
class Program
{
    static void Main()
    {
        List<Student> students = new List<Student>
        {
            new Student { Name = "田中", Age = 20 },
            new Student { Name = "佐藤", Age = 18 },
            new Student { Name = "鈴木", Age = 22 },
            new Student { Name = "山田", Age = 21 }
        };
        
        var sortedStudents = students.Where(s => s.Age >= 20) // 年齢が20歳以上の学生をフィルタリング
                                     .OrderBy(s => s.Age) // 年齢でソート
                                     .ThenBy(s => s.Name); // 名前で追加ソート
        
        foreach (var student in sortedStudents)
        {
            Console.WriteLine($"{student.Name}, {student.Age}歳");
        }
    }
}
田中, 20歳
山田, 21歳
鈴木, 22歳

これらの応用例を通じて、ThenByメソッドの柔軟な使い方を理解することができます。

複雑なデータ構造や動的な条件に基づいたソートが可能であり、データの整理や抽出がより効果的に行えることがわかります。

ThenByメソッドのパフォーマンス

大規模データセットでのパフォーマンス

ThenByメソッドは、LINQを使用してデータをソートする際に非常に便利ですが、大規模データセットに対して使用する場合、パフォーマンスに影響を与える可能性があります。

特に、データセットが数万件以上になると、ソート処理にかかる時間が増加します。

LINQのソートメソッドは、内部的にクイックソートやマージソートなどのアルゴリズムを使用しており、これらは平均的にはO(n log n)の時間計算量を持ちます。

しかし、最悪の場合はO(n^2)になることもあるため、データの特性やソート条件によってはパフォーマンスが低下することがあります。

大規模データセットを扱う際は、必要に応じてデータを分割したり、並列処理を検討することが重要です。

OrderByとThenByの組み合わせによる影響

OrderByメソッドThenByメソッドを組み合わせて使用する場合、最初のOrderByメソッドでのソート結果がThenByメソッドの動作に影響を与えます。

OrderByメソッドは、最初のソートを行い、ThenByメソッドはその結果に基づいて追加のソートを行います。

このため、OrderByメソッドでのソートが複雑であるほど、ThenByメソッドのパフォーマンスにも影響が出る可能性があります。

特に、複数のプロパティを持つオブジェクトをソートする場合、各プロパティの比較が行われるため、全体の処理時間が増加します。

したがって、ソート条件を最適化し、必要なプロパティのみを使用することが推奨されます。

ソートの安定性について

ThenByメソッドを使用する際のソートの安定性は重要なポイントです。

安定なソートとは、同じキーを持つ要素の相対的な順序が保持されることを意味します。

LINQのOrderByおよびThenByメソッドは、安定なソートを提供します。

つまり、最初のOrderByメソッドでソートされた結果に対してThenByメソッドを適用した場合、同じキーを持つ要素の順序は変わりません。

これにより、複数の条件でソートを行う際に、期待通りの結果が得られます。

安定性が必要な場合は、ThenByメソッドを使用することで、データの整列がより信頼性の高いものになります。

ThenByメソッドを使う際の注意点

null値の扱い

ThenByメソッドを使用する際、null値の扱いには注意が必要です。

デフォルトでは、LINQのソートメソッドはnull値を最小値として扱います。

つまり、null値は他の値よりも前に配置されます。

これにより、null値を含むリストをソートすると、期待しない順序になることがあります。

例えば、以下のようなコードでは、null値が最初に表示されます。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<string> words = new List<string> { "apple", null, "banana", "kiwi" };
        
        var sortedWords = words.OrderBy(w => w) // 昇順でソート
                               .ThenBy(w => w); // ThenByで追加ソート
        
        foreach (var word in sortedWords)
        {
            Console.WriteLine(word ?? "null"); // nullの場合は"null"と表示
        }
    }
}
null
apple
banana
kiwi

null値を最後に配置したい場合は、カスタムコンパレータを使用するか、Whereメソッドでフィルタリングする必要があります。

ソート順が期待通りにならない場合

ThenByメソッドを使用しても、ソート順が期待通りにならない場合があります。

これは、ソート条件が適切に設定されていない場合や、データの特性によるものです。

例えば、数値や文字列のソートでは、昇順と降順の指定を間違えると、意図しない順序になることがあります。

以下の例では、年齢でソートした後に名前でソートしていますが、名前のソートが期待通りでない場合があります。

using System;
using System.Collections.Generic;
using System.Linq;
class Student
{
    public string Name { get; set; }
    public int Age { get; set; }
}
class Program
{
    static void Main()
    {
        List<Student> students = new List<Student>
        {
            new Student { Name = "田中", Age = 20 },
            new Student { Name = "佐藤", Age = 20 },
            new Student { Name = "鈴木", Age = 21 },
            new Student { Name = "山田", Age = 21 }
        };
        
        var sortedStudents = students.OrderBy(s => s.Age) // 年齢でソート
                                     .ThenBy(s => s.Name.Length); // 名前の長さで追加ソート
        
        foreach (var student in sortedStudents)
        {
            Console.WriteLine($"{student.Name}, {student.Age}歳");
        }
    }
}
田中, 20歳
佐藤, 20歳
鈴木, 21歳
山田, 21歳

この場合、名前の長さでソートしているため、同じ年齢の学生の順序が期待通りでないことがあります。

ソート条件を見直すことが重要です。

ソート条件が重複する場合の挙動

ThenByメソッドを使用する際、ソート条件が重複する場合の挙動にも注意が必要です。

複数の条件でソートを行う際、同じキーを持つ要素が存在する場合、ThenByメソッドはその要素の相対的な順序を保持します。

これにより、同じ条件でソートされた要素の順序が変わらないことが保証されます。

以下の例では、年齢でソートした後に名前でソートしていますが、同じ年齢の学生がいる場合、名前の順序は保持されます。

using System;
using System.Collections.Generic;
using System.Linq;
class Student
{
    public string Name { get; set; }
    public int Age { get; set; }
}
class Program
{
    static void Main()
    {
        List<Student> students = new List<Student>
        {
            new Student { Name = "田中", Age = 20 },
            new Student { Name = "佐藤", Age = 20 },
            new Student { Name = "鈴木", Age = 21 },
            new Student { Name = "山田", Age = 21 }
        };
        
        var sortedStudents = students.OrderBy(s => s.Age) // 年齢でソート
                                     .ThenBy(s => s.Name); // 名前で追加ソート
        
        foreach (var student in sortedStudents)
        {
            Console.WriteLine($"{student.Name}, {student.Age}歳");
        }
    }
}
田中, 20歳
佐藤, 20歳
鈴木, 21歳
山田, 21歳

このように、ThenByメソッドを使用することで、同じキーを持つ要素の順序が保持されるため、データの整列がより信頼性の高いものになります。

ただし、重複する条件が多い場合、ソートのパフォーマンスに影響を与えることがあるため、注意が必要です。

よくある質問

ThenByとOrderByの違いは何ですか?

ThenByメソッドOrderByメソッドの主な違いは、ソートの順序を決定するタイミングにあります。

OrderByメソッドは、シーケンスの最初のソートを行うために使用され、最初のキーに基づいてデータを昇順に整列します。

一方、ThenByメソッドは、既にソートされたシーケンスに対して追加のソートを行うために使用されます。

つまり、ThenByはOrderByの後に続くメソッドであり、複数の条件でのソートを実現するために使われます。

例えば、最初に年齢でソートし、その後名前でソートする場合、最初のソートにはOrderByを使用し、次のソートにはThenByを使用します。

ThenByDescendingはどのように使いますか?

ThenByDescendingメソッドは、ThenByメソッドと同様に追加のソートを行いますが、降順でソートを行う点が異なります。

ThenByDescendingを使用することで、既にソートされたシーケンスに対して、指定したプロパティを降順でソートすることができます。

例えば、年齢で昇順にソートした後、名前を降順でソートする場合、以下のように記述します。

var sortedStudents = students.OrderBy(s => s.Age) // 年齢で昇順にソート
                             .ThenByDescending(s => s.Name); // 名前で降順に追加ソート

このように、ThenByDescendingを使うことで、柔軟にソート条件を指定することができます。

ソート条件が複数ある場合、どのように指定すればよいですか?

ソート条件が複数ある場合、OrderByメソッドThenByメソッドを組み合わせて使用します。

最初にOrderByメソッドで最初のソート条件を指定し、その後にThenByメソッドで追加のソート条件を指定します。

さらに、必要に応じてThenByDescendingメソッドを使用して降順でのソートも可能です。

以下の例では、年齢で昇順にソートした後、名前で昇順にソートしています。

var sortedStudents = students.OrderBy(s => s.Age) // 年齢で昇順にソート
                             .ThenBy(s => s.Name); // 名前で昇順に追加ソート

このように、複数の条件を組み合わせることで、データをより細かく整理することができます。

ソート条件は必要に応じて追加することができ、柔軟なデータ整列が可能です。

まとめ

この記事では、C#のLINQにおけるThenByメソッドの使い方や応用、パフォーマンスに関する注意点について詳しく解説しました。

ThenByメソッドを活用することで、複数の条件に基づいた柔軟なデータのソートが可能になり、特に大規模データセットにおいても効果的に情報を整理することができます。

これを機に、実際のプロジェクトやデータ処理の場面でThenByメソッドを積極的に活用し、より効率的なデータ管理を目指してみてください。

  • URLをコピーしました!
目次から探す