あまり見かけないけど便利な LINQ メソッド を使いこなす！

（**僕の周りで**）あまり見かけないけど、使いこなせれば強力な LINQ メソッドの紹介です。

## `Zip`

2つのシーケンスをインデックス順にマージして一つのシーケンスにします。

```c#
int[] numbers = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three" }; 

var zipped = numbers.Zip(
    words,
    (n, w) => $"{n} {w}"
);

foreach (var item in zipped)
    Console.WriteLine(item);
```

```:結果
1 one
2 two
3 three
```

はみ出る要素は無視されます。

---

## `Union` `Intersect` `Except`

`Union`は2つのシーケンスの少なくともどちらかに含まれている要素（和集合）のシーケンスを返します。

```c#
string[] langs1 = { "C#", "Rust", "Elixir", "Swift", "Kotlin", };
string[] langs2 = { "Rust", "Clojure", "C#", "OCaml", };

var union = langs1.Union(langs2);

foreach (var lang in union)
    Console.Write($"{lang} ");
```

```:結果
C# Rust Elixir Swift Kotlin Clojure OCaml
```

`Intersect`は2つのシーケンスのどちらにも含まれる要素（積集合）のシーケンスを返します。

```c#
string[] langs1 = { "C#", "Rust", "Elixir", "Swift", "Kotlin", };
string[] langs2 = { "Rust", "Clojure", "C#", "OCaml", };

var intersect = langs1.Intersect(langs2);

foreach (var lang in intersect)
    Console.Write($"{lang} ");
```

```:結果
C# Rust
```
`Except`は対象のシーケンスからもう一方のシーケンスに属する要素を取り除いたシーケンスを返します。

```c#
 string[] langs1 = { "C#", "Rust", "Elixir", "Swift", "Kotlin", };
 string[] langs2 = { "Rust", "Clojure", "C#", "OCaml", };

 var except = langs1.Except(langs2);

 foreach (var lang in except)
     Console.Write($"{lang} ");
```

```:結果
Elixir Swift Kotlin
```

シーケンスの取り除かれる側と取り除く側を入れ替えると、結果も変わります。

```C#
string[] langs1 = { "C#", "Rust", "Elixir", "Swift", "Kotlin", };
string[] langs2 = { "Rust", "Clojure", "C#", "OCaml", };

var except = langs2.Except(langs1);

foreach (var lang in except)
    Console.Write($"{lang} ");
```

```:結果
Clojure OCaml
```

---

# `ToDictionary` `ToLookup`

`ToDictionary`はシーケンスや配列などを`Dictionary`に変換します。
`Dictionary`型に変換することでキーで高速に値にアクセスできます。

以下のような`Book`クラスのリストがあるとします。

```c#
var books = new List<Book>
{
    new Book {
        ISBN = "ISBN● - AAAA - BBBB - C",
        Title = "タイトル",
        Author = "著者名",
        PublishedYear = 2000
    },
    new Book { … },
    new Book { … },
    ⋮
};
```

```c#
// ISBN をキー、値を Book オブジェクトとする Dictionary を作る
var bookDict = books.ToDictionary(b => b.ISBN);

// 値を指定するには、第2引数にラムダ式を与える
var bookDict = books.ToDictionary(b => b.ISBN, b => b.Title);
```

`ToLookup`はシーケンスや配列などを`ILookup`型に変換します。
`Dictionary`はキーに対し1つの値が割り当てられますが、
`ILookUp`はキーに対しコレクションが割り当てられます。

```c#
// 発行年をキーとし、Book オブジェクトのコレクションが値となる ILookup を作成する
var lookup = books.ToLookup(b => b.PublishedYear);
lookup[2000] // => 発行年が2000年の book オブジェクト一覧が返る

// 第二引数にラムダ式を与えることで、値を指定できる
var lookup = books.ToLookup(b => b.PublishedYear, b => b.Title);
```

`ToLookup`は`GroupBy`とよく似ていますが、`ToLookup`は即時実行、`GroupBy`は遅延評価です。
`ToLookup`なら`lookup[2000]`のようにインデクサでアクセスできます。

値にリストを持つ`Dictionary`を作る場合には`GroupBy`と`ToDictionary`を使います。
`ToLookUp`と`ToDcitinary`を組み合わせると2度ループが回ってしまいます。

```c#
// ToLookup と ToDictionary で2度ループが回る
var dic = books
    .ToLookup(b => b.PublishedYear)
    .ToDictionary(
        group => group.Key,
        group => group.ToList()
    );

// GroupBy と ToDictionary なら ループが回るのは ToDictionary の一度だけ
var dic = books
    .GroupBy(b => b.PublishedYear)
    .ToDictionary(
        group => group.Key,
        group => group.ToList()
    );
```

---

# `Distinct`

メジャーかもしれませんが、僕の周りではあまり見ないので載せます。
シーケンスから重複した要素を削除したシーケンスを返します。

```c#
int[] ints = { 1, 2, 3, 4, 3, 2, 2, 5, 4 };
var distinct = ints.Distinct();
foreach (var n in distinct)
    Console.Write($"{n} ");
```

```:結果
1 2 3 4 5
```

---

## `GroupJoin`

2つのシーケンスをグルーピングする`GroupBy`と、
2つのシーケンスを結合する`Join`を、同時にやってしまうメソッドです。

以下のような漫画雑誌と漫画のリストがあるとします。

```c#
var magazines = new[]
{
    new { Name = "ジャンプ" },
    new { Name = "サンデー"},
    new { Name = "チャンピオン" },
};
var myComics = new[]
{
    new { Title = "グラップラー刃牙", Publication = "チャンピオン" },
    new { Title = "スラムダンク", Publication = "ジャンプ" },
    new { Title = "HUNTER×HUNTER", Publication = "ジャンプ" },
    new { Title = "名探偵コナン", Publication = "サンデー" }
};
```

上記2つのリストを結合し、漫画雑誌毎に漫画を振り分けます。

```c#
var groups = magazines.GroupJoin(
    myComics,
    magazine => magazine.Name,
    comic => comic.Publication,
    (magazine, comics) => new
    {
        Magazine = magazine.Name,
        Comics = comics
    });

foreach (var group in groups)
{
    Console.WriteLine($"{group.Magazine}:");
    foreach (var comic in group.Comics)
        Console.WriteLine($"  - {comic.Title}");
}
```

```:結果
ジャンプ:
  - スラムダンク
  - HUNTER×HUNTER
サンデー:
  - 名探偵コナン
チャンピオン:
  - グラップラー刃牙
```

---

## まとめ

LINQ には便利なメソッドがたくさんあります。
普段よく使うもの以外にもハマるとシーケンス・コレクション操作を強力にサポートしてくれるものがたくさんあるので、ぜひ使いこなしたいです。