C# Tips | コレクション・LINQ:ソート

C# C#
スポンサーリンク

はじめに:「ソート」は“人間が読みやすい順番に並べ替える作業”

業務システムでは、一覧画面・帳票・CSV 出力など、
「同じデータでも、どの順番で並んでいるか」がものすごく大事になります。

社員一覧なら「社員番号順」
売上一覧なら「日付の昇順」
エラー一覧なら「重要度の高い順」

こういった「並び順」をコードで表現するのが、ソート(並べ替え)です。

C# では、
配列や List に対する「破壊的ソート(中身を書き換える)」と、
LINQ による「新しい並び順の列を作るソート」
の2つの考え方があります。

ここでは初心者向けに、まず LINQ のソートから入り、
そのあと List の Sort、複数キーでのソートまで、かみ砕いて説明していきます。


基本:LINQ の OrderBy / OrderByDescending でソートする

数値や文字列を昇順・降順に並べる

一番シンプルな例からいきます。

using System;
using System.Collections.Generic;
using System.Linq;

var numbers = new List<int> { 5, 2, 9, 1, 3 };

var asc  = numbers.OrderBy(x => x);        // 昇順
var desc = numbers.OrderByDescending(x => x); // 降順

Console.WriteLine("昇順:");
foreach (var n in asc)
{
    Console.WriteLine(n); // 1, 2, 3, 5, 9
}

Console.WriteLine("降順:");
foreach (var n in desc)
{
    Console.WriteLine(n); // 9, 5, 3, 2, 1
}
C#

ここでの重要ポイントは、「OrderBy は“元の List を書き換えない”」ということです。
numbers 自体の順番は変わらず、OrderBy の結果として“並び替えられた列”が新しく返ってきます。

文字列も同じように並べ替えられる

var names = new List<string> { "Charlie", "Alice", "Bob" };

var sorted = names.OrderBy(x => x);

foreach (var n in sorted)
{
    Console.WriteLine(n); // Alice, Bob, Charlie
}
C#

OrderBy(x => x) の「x => x」は、
「要素そのものをキーとして使う」という意味です。
「何を基準に並べるか」をこの部分で指定します。


オブジェクトを「特定のプロパティ」でソートする

社員を「社員番号順」「名前順」で並べる

業務では、単純な int や string ではなく、
クラスのリストをソートすることがほとんどです。

例えば、こんなクラスがあるとします。

public class Employee
{
    public int No { get; set; }          // 社員番号
    public string Name { get; set; } = "";
}
C#

社員のリストを作ります。

var employees = new List<Employee>
{
    new Employee { No = 3, Name = "Tanaka" },
    new Employee { No = 1, Name = "Sato" },
    new Employee { No = 2, Name = "Suzuki" },
};
C#

社員番号順に並べたいときは、こう書きます。

var byNo = employees.OrderBy(e => e.No);

foreach (var e in byNo)
{
    Console.WriteLine($"{e.No}: {e.Name}");
    // 1: Sato
    // 2: Suzuki
    // 3: Tanaka
}
C#

名前の昇順なら、こうです。

var byName = employees.OrderBy(e => e.Name);
C#

ここでの重要ポイントは、「OrderBy の中で“ソートキー”を選ぶ」という感覚です。
e => e.No なら「社員番号で並べる」、
e => e.Name なら「名前で並べる」という意味になります。


複数キーでソートする:ThenBy / ThenByDescending

まず部署、次に社員番号、のような並び順

業務では、「部署コード順 → 部署内では社員番号順」のように、
複数の条件でソートしたいことがよくあります。

例えば、こんなクラスを考えます。

public class Employee
{
    public string Department { get; set; } = ""; // 部署コード
    public int No { get; set; }                  // 社員番号
    public string Name { get; set; } = "";
}
C#

データを用意します。

var employees = new List<Employee>
{
    new Employee { Department = "B", No = 3, Name = "Tanaka" },
    new Employee { Department = "A", No = 2, Name = "Suzuki" },
    new Employee { Department = "A", No = 1, Name = "Sato" },
    new Employee { Department = "B", No = 1, Name = "Yamada" },
};
C#

「部署コードの昇順 → 同じ部署内では社員番号の昇順」で並べたいときは、
OrderByThenBy を組み合わせます。

var sorted = employees
    .OrderBy(e => e.Department) // まず部署でソート
    .ThenBy(e => e.No);         // 次に社員番号でソート

foreach (var e in sorted)
{
    Console.WriteLine($"{e.Department}-{e.No}: {e.Name}");
    // A-1: Sato
    // A-2: Suzuki
    // B-1: Yamada
    // B-3: Tanaka
}
C#

ここでの重要ポイントは、「ソートの優先順位は、OrderByThenBy の順に決まる」ということです。
OrderBy が“第一キー”、ThenBy が“第二キー”、さらに ThenBy を重ねれば第三キー…と続けられます。

降順を混ぜたいときは、OrderByDescendingThenByDescending を使います。

var sorted = employees
    .OrderBy(e => e.Department)
    .ThenByDescending(e => e.No); // 部署内では社員番号の降順
C#

List<T>.Sort と LINQ のソートの違い

List.Sort は「その List 自体を書き換える」

LINQ の OrderBy は「新しい並び順の列を返す」ものでしたが、
List<T> には「自分自身を並べ替える」Sort メソッドもあります。

var numbers = new List<int> { 5, 2, 9, 1, 3 };

numbers.Sort(); // numbers 自体が昇順に並び替えられる

foreach (var n in numbers)
{
    Console.WriteLine(n); // 1, 2, 3, 5, 9
}
C#

ここでの重要ポイントは、「Sort は破壊的(in-place)である」ということです。
元の順番が必要な場合は、Sort を使う前に ToList() などでコピーを作る必要があります。

オブジェクトを Sort するには「比較ルール」が必要

List<T>.Sort でオブジェクトを並べ替える場合、
Comparison<T>IComparer<T> を渡して「どう比べるか」を教えてあげます。

社員番号順に並べる例です。

var employees = new List<Employee>
{
    new Employee { No = 3, Name = "Tanaka" },
    new Employee { No = 1, Name = "Sato" },
    new Employee { No = 2, Name = "Suzuki" },
};

employees.Sort((x, y) => x.No.CompareTo(y.No));

foreach (var e in employees)
{
    Console.WriteLine($"{e.No}: {e.Name}");
}
C#

(x, y) => x.No.CompareTo(y.No) は、
「x のほうが小さければ負の値、同じなら 0、大きければ正の値を返す」比較関数です。

実務では、
「元の List をそのまま並べ替えたいときは Sort
「元の List はそのままにしておきたいときは LINQ の OrderBy
という使い分けをすると分かりやすいです。


実務で意識してほしいポイント

「何順か」をコードにハッキリ書く

業務コードで一番もったいないのは、
「なんとなくソートしているけど、何順なのかが読み取れない」状態です。

OrderBy(e => e.No) なら「社員番号順」
OrderBy(e => e.Department).ThenBy(e => e.No) なら「部署→社員番号順」

というように、「何を基準に並べているか」がコードから一目で分かるように書くことが大事です。

ソートは「画面用」「集計用」など用途ごとに分ける

同じデータでも、
画面では「名前順」、CSV では「ID 順」、集計では「日付順」
といったように、用途によって並び順が変わります。

そのときは、

画面用のソートロジック
CSV 用のソートロジック

のように、メソッドや拡張メソッドとして分けておくと、
「どこでどんな順番にしているか」が追いやすくなります。


まとめ:「ソートユーティリティ」は“データに意味のある順番を与える道具”

ソートの本質は、
「ただの集合に、“人間にとって意味のある順番”を与えること」です。

C# / LINQ では、

OrderBy / OrderByDescending で昇順・降順の並び替えができる
ThenBy / ThenByDescending で複数キーのソートが書ける
List<T>.Sort は「その List 自体を書き換える」ので、元の順番が不要なときに向いている

という道具が揃っています。

大事なのは、「何を基準に」「どんな優先順位で」並べたいのかを、
まず日本語でハッキリさせてから、それを OrderBy / ThenBy に落とし込むことです。
そこさえブレなければ、ソートはとても素直で強力な“業務ユーティリティ”になります。

タイトルとURLをコピーしました