C# の LINQ で並べ替える OrderBy

OrderBy はシーケンスの要素を昇順に並べ替えるメソッドだ。降順にしたい場合は OrderByDescending を使う。

基本的な使い方

数値を昇順で並べ替える。

int[] numbers = { 5, 2, 8, 1, 9, 3 };

var sorted = numbers.OrderBy(n => n);

foreach (var n in sorted)
{
    Console.Write($"{n} ");  // 1 2 3 5 8 9
}

ラムダ式でソートのキーを指定する。この例では要素そのものをキーにしている。

降順に並べ替える

OrderByDescending を使えば降順になる。

int[] numbers = { 5, 2, 8, 1, 9, 3 };

var sorted = numbers.OrderByDescending(n => n);

foreach (var n in sorted)
{
    Console.Write($"{n} ");  // 9 8 5 3 2 1
}

メソッド名が変わるだけで、使い方は同じだ。

オブジェクトの並べ替え

プロパティをキーにして並べ替えることが多い。

var people = new List<Person>
{
    new Person { Name = "Charlie", Age = 35 },
    new Person { Name = "Alice", Age = 25 },
    new Person { Name = "Bob", Age = 30 }
};

// 年齢で昇順
var byAge = people.OrderBy(p => p.Age);

foreach (var person in byAge)
{
    Console.WriteLine($"{person.Name}: {person.Age}");
}
// Alice: 25
// Bob: 30
// Charlie: 35

年齢をキーにして並べ替えている。名前で並べ替えたければ p => p.Name とすればいい。

複数条件での並べ替え

ThenByThenByDescending を使えば、第二、第三のソートキーを指定できる。

var people = new List<Person>
{
    new Person { Name = "Alice", Age = 25, City = "Tokyo" },
    new Person { Name = "Bob", Age = 30, City = "Osaka" },
    new Person { Name = "Charlie", Age = 25, City = "Tokyo" },
    new Person { Name = "Diana", Age = 30, City = "Tokyo" }
};

// 年齢で昇順、同じ年齢なら名前で昇順
var sorted = people
    .OrderBy(p => p.Age)
    .ThenBy(p => p.Name);

foreach (var person in sorted)
{
    Console.WriteLine($"{person.Name}: {person.Age}");
}
// Alice: 25
// Charlie: 25
// Bob: 30
// Diana: 30

最初に年齢で並べ、年齢が同じ場合は名前のアルファベット順になる。

OrderBy + ThenBy

第一キーで昇順、第二キーで昇順

OrderBy + ThenByDescending

第一キーで昇順、第二キーで降順

OrderByDescending + ThenBy

第一キーで降順、第二キーで昇順

文字列の並べ替え

文字列はデフォルトで辞書順(アルファベット順)に並ぶ。

string[] fruits = { "banana", "Apple", "cherry", "apricot" };

var sorted = fruits.OrderBy(f => f);

foreach (var fruit in sorted)
{
    Console.WriteLine(fruit);
}
// Apple, apricot, banana, cherry

注意点として、大文字は小文字より前に来る(ASCII 順)。大文字小文字を区別せずに並べたい場合は、StringComparer を指定する。

string[] fruits = { "banana", "Apple", "cherry", "apricot" };

var sorted = fruits.OrderBy(f => f, StringComparer.OrdinalIgnoreCase);

foreach (var fruit in sorted)
{
    Console.WriteLine(fruit);
}
// Apple, apricot, banana, cherry

null の扱い

ソートキーに null が含まれる場合、null は最小値として扱われ、昇順では先頭に来る。

var people = new List<Person>
{
    new Person { Name = "Bob" },
    new Person { Name = null },
    new Person { Name = "Alice" }
};

var sorted = people.OrderBy(p => p.Name);

foreach (var person in sorted)
{
    Console.WriteLine(person.Name ?? "(null)");
}
// (null)
// Alice
// Bob

null を最後に持っていきたい場合は、カスタムロジックを使う必要がある。

安定ソート

LINQ の OrderBy は安定ソート(stable sort)だ。同じキー値を持つ要素は、元の順序が維持される。

var items = new List<(string Name, int Priority)>
{
    ("A", 1),
    ("B", 2),
    ("C", 1),
    ("D", 2)
};

var sorted = items.OrderBy(x => x.Priority);

foreach (var item in sorted)
{
    Console.WriteLine($"{item.Name}: {item.Priority}");
}
// A: 1 (元は1番目)
// C: 1 (元は3番目)
// B: 2 (元は2番目)
// D: 2 (元は4番目)

同じ Priority を持つ A と C、B と D は、元の登場順が保たれている。これは複数条件でソートする際に重要な性質となる。