LoginSignup
27
28

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-06-20
1 / 7

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

Zip

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

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つのシーケンスの少なくともどちらかに含まれている要素(和集合)のシーケンスを返します。

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つのシーケンスのどちらにも含まれる要素(積集合)のシーケンスを返します。

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は対象のシーケンスからもう一方のシーケンスに属する要素を取り除いたシーケンスを返します。

 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

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

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クラスのリストがあるとします。

var books = new List<Book>
{
    new Book {
        ISBN = "ISBN● - AAAA - BBBB - C",
        Title = "タイトル",
        Author = "著者名",
        PublishedYear = 2000
    },
    new Book {  },
    new Book {  },
    
};
// 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はキーに対しコレクションが割り当てられます。

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

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

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

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

// 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

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

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を、同時にやってしまうメソッドです。

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

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つのリストを結合し、漫画雑誌毎に漫画を振り分けます。

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

27
28
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
28