C#ソートメソッドまとめ

# はじめに
C#で使用するソート系メソッドをまとめました。
個人的な備忘録です。間違いなどあれば教えていただけると嬉しいです。

## 必要なusing文

```csharp
using System;                    // Array.Sort用
using System.Collections.Generic; // List<T>用
using System.Linq;               // OrderBy, Where, Select用
```

## 配列のソート

### Array.Sort() - 破壊的
```csharp
int[] array = { 5, 2, 8, 1, 9 };
Array.Sort(array);               // 元の配列が変更される
// 結果: { 1, 2, 5, 8, 9 }
```

### OrderBy() - 非破壊的
```csharp
int[] array = { 5, 2, 8, 1, 9 };
var sorted = array.OrderBy(x => x).ToArray();
// 元の配列: { 5, 2, 8, 1, 9 } (変更されない)
// sorted: { 1, 2, 5, 8, 9 }
```

### 元データを保持したい場合
```csharp
int[] original = { 5, 2, 8, 1, 9 };

// 方法1: Clone()を使用
int[] copy = (int[])original.Clone();
Array.Sort(copy);

// 方法2: OrderBy()を使用
var sorted = original.OrderBy(x => x).ToArray();
```

## Listのソート

### List.Sort() - 破壊的
```csharp
var list = new List<int> { 5, 2, 8, 1, 9 };
list.Sort();                     // 元のリストが変更される
// 結果: { 1, 2, 5, 8, 9 }
```

### OrderBy() - 非破壊的
```csharp
var list = new List<int> { 5, 2, 8, 1, 9 };
var sorted = list.OrderBy(x => x).ToList();
// 元のリスト: { 5, 2, 8, 1, 9 } (変更されない)
// sorted: { 1, 2, 5, 8, 9 }
```

### カスタムソート
```csharp
var list = new List<int> { 5, 2, 8, 1, 9 };

// 降順
list.Sort((x, y) => y.CompareTo(x));

// 絶対値でソート
list.Sort((x, y) => Math.Abs(x).CompareTo(Math.Abs(y)));
```

## LINQ基本メソッド

### OrderBy() / OrderByDescending()
```csharp
var numbers = new List<int> { 5, 2, 8, 1, 9 };

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

### ThenBy() / ThenByDescending()
```csharp
var people = new List<(string Name, int Age)>
{
    ("田中", 25), ("佐藤", 25), ("鈴木", 30)
};

var sorted = people
    .OrderBy(x => x.Age)      // まず年齢でソート
    .ThenBy(x => x.Name)      // 同じ年齢なら名前でソート
    .ToList();
```

### Where() - フィルタリング
```csharp
var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

var evenNumbers = numbers.Where(x => x % 2 == 0);     // 偶数のみ
var greaterThan5 = numbers.Where(x => x > 5);         // 5より大きい
var range = numbers.Where(x => x >= 3 && x <= 7);     // 3以上7以下
```

### Select() - 変換
```csharp
var numbers = new List<int> { 1, 2, 3, 4, 5 };

var doubled = numbers.Select(x => x * 2);              // 2倍
var strings = numbers.Select(x => $"数字{x}");         // 文字列変換
var squared = numbers.Select(x => x * x);              // 平方
```

### Reverse() - 順序逆転
```csharp
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var reversed = numbers.Reverse();                      // 逆順
```

## タプルのソート

### 基本的なタプルソート
```csharp
var dates = new List<(int Month, int Day)>
{
    (3, 29), (1, 1), (1, 4), (12, 28), (4, 1)
};

// 月→日の順でソート
var sorted = dates.OrderBy(x => x.Month).ThenBy(x => x.Day).ToList();
// 結果: (1,1), (1,4), (3,29), (4,1), (12,28)
```

### 複雑なタプルソート
```csharp
var people = new List<(string Name, int Age, string City)>
{
    ("田中", 25, "東京"), ("佐藤", 30, "大阪"), ("鈴木", 25, "東京")
};

// 年齢→名前の順でソート
var sorted = people.OrderBy(x => x.Age).ThenBy(x => x.Name).ToList();

// 都市→年齢→名前の順でソート
var multiSort = people
    .OrderBy(x => x.City)
    .ThenBy(x => x.Age)
    .ThenBy(x => x.Name)
    .ToList();
```

### 4月2日から始まる年間カレンダーソート
```csharp
var dates = new List<(int Month, int Day)>
{
    (3, 29), (1, 1), (1, 4), (12, 28), (4, 1), (4, 2)
};

// 4月2日を基準とした並び順
var yearFromApril = dates.OrderBy(x => 
{
    if (x.Month > 4 || (x.Month == 4 && x.Day >= 2))
        return x.Month * 100 + x.Day;      // 今年（ソート用キー値）
    else
        return (x.Month + 12) * 100 + x.Day; // 翌年扱い（ソート用キー値）
    // ※この計算はソート順を決めるためだけで、元のタプルは変更されない
}).ToList();
// 結果: (4,2), (12,28), (1,1), (1,4), (3,29), (4,1)
```

## メソッドチェーン

```csharp
var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

var result = numbers
    .Where(x => x % 2 == 0)        // 偶数のみ
    .Select(x => x * 3)            // 3倍
    .OrderByDescending(x => x)     // 降順
    .Take(3)                       // 上位3つ
    .ToList();
// 結果: { 30, 24, 18 }
```

## ソート後の比較
```csharp
var list1 = new List<int> { 5, 2, 8, 1, 9 };
var list2 = new List<int> { 1, 2, 5, 8, 9 };

// == では比較できない（参照比較のため）
Console.WriteLine(list1 == list2); // False（中身が同じでも異なるインスタンス）

// SequenceEqual で順序も含めて比較
Console.WriteLine(list1.SequenceEqual(list2)); // False（順序が違う）

// 先にソートしてから比較（順序無視で要素を比較）
var sortedList1 = list1.OrderBy(x => x).ToList();
var sortedList2 = list2.OrderBy(x => x).ToList();
var areEqualWhenSorted = sortedList1.SequenceEqual(sortedList2);
Console.WriteLine(areEqualWhenSorted); // True
```


## 使い分けガイド

### 元データを変更してもよい場合
- **配列**: `Array.Sort()`
- **List**: `list.Sort()`

### 元データを保持したい場合
- **配列**: `array.OrderBy(x => x).ToArray()`
- **List**: `list.OrderBy(x => x).ToList()`

### 複雑な条件でソート
- **共通**: `OrderBy()` + `ThenBy()`

### Dictionaryのソート
```csharp
var dict = new Dictionary<string, int> { {"a", 3}, {"b", 1}, {"c", 2} };

var sortedByKey = dict.OrderBy(x => x.Key);      // キーでソート
var sortedByValue = dict.OrderBy(x => x.Value);  // 値でソート
```

## 注意点

### 破壊的メソッド（元データ変更）
- `Array.Sort()`
- `List.Sort()`
- `List.Reverse()`
- `Array.Reverse()`

### 非破壊的メソッド（元データ保持）
- `OrderBy()`系
- `Where()`
- `Select()`
- `Reverse()`（LINQ版）

### OrderbyやWhereなどは遅延実行される
```csharp
var query = numbers.Where(x => x > 5);  // まだ実行されない
var result = query.ToList();            // ここで実行される
```

## よく使うパターン

### 年齢でソート、同じ年齢なら名前順
```csharp
people.OrderBy(p => p.Age).ThenBy(p => p.Name)
```

### 偶数のみ取得して降順ソート
```csharp
numbers.Where(x => x % 2 == 0).OrderByDescending(x => x)
```

### 名前を取得してアルファベット順
```csharp
people.Select(p => p.Name).OrderBy(name => name)
```

### 上位3件を取得
```csharp
numbers.OrderByDescending(x => x).Take(3)
```

## 対応表

| コレクション | Sort() | OrderBy() | 専用メソッド |
|-------------|--------|-----------|-------------|
| int[] | ❌ | ✅ | Array.Sort() |
| List<T> | ✅ | ✅ | - |
| Dictionary | ❌ | ✅ | - |
| 配列・リスト以外※ | ❌ | ✅ | - |

※IEnumerable<T>を実装する型（HashSet、Queue、Stack等）