連載:[完全版]究極のC#プログラミング

Chapter15 LINQとクエリ式

川俣 晶
2010/03/17
Page 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

15.18 join句の左外部結合

 グループ化結合は上記のように二重の繰り返しを必要とする。これは、ある商品を販売している店舗一覧を知りたい場合には便利だが、単に「商品」と「販売店」の組み合わせリストだけがほしい場合は冗長である。

 そこで、さらにクエリ式にfrom句をもう1つ追加することで、二重の繰り返しを再度単層の繰り返しに変換させることができるリスト15.22参照)。

using System;
using System.Linq;

class Program
{
  class 商品情報
  {
    public int Id;
    public string 名前;
  }

  class 商品販売価格
  {
    public int Id;
    public string 店名;
  }

  static void Main(string[] args)
  {
    商品情報[] 商品情報データ =
    {
      new 商品情報() { Id=1, 名前="PC-8001" },
      new 商品情報() { Id=2, 名前="MZ-80K" },
      new 商品情報() { Id=3, 名前="Basic Master Level-3" },
      new 商品情報() { Id=4, 名前="COMKIT 8060" },
    };

    商品販売価格[] 商品販売価格データ =
    {
      new 商品販売価格() { Id=1, 店名="BitOut" },
      new 商品販売価格() { Id=1, 店名="富士山音響" },
      new 商品販売価格() { Id=2, 店名="富士山音響"  },
      new 商品販売価格() { Id=3, 店名="マイコンセンターROM" },
      new 商品販売価格() { Id=3, 店名="富士山音響"  },
    };

    var query = from x in 商品情報データ
                join y in 商品販売価格データ
                                  on x.Id equals y.Id into z
                from a in z
                select new { Name = x.名前, 販売店 = a };

    foreach (var 商品 in query)
    {
      Console.WriteLine("{0}", 商品.Name);
      Console.WriteLine("\t{0}", 商品.販売店.店名);
    }
  }
}
リスト15.22 単層の繰り返し

PC-8001
    BitOut
PC-8001
    富士山音響
MZ-80K
    富士山音響
Basic Master Level-3
    マイコンセンターROM
Basic Master Level-3
    富士山音響
リスト15.22の実行結果

 つまり、「列挙」である範囲変数zをさらにfrom句で個々の値に分解することで、select句は列挙を含まない結果を生成することができ、結果として二重の列挙を発生させないで済む。

 しかし、このコードには1つだけ決定的な不満がある。それは、join句の右側に対応する項目(店名)がない左側の項目(名前)は出力されないことである。この例では、「COMKIT 8060」という名前が実行結果にまったく出てこない。

 この問題を解決する結合が、「左外部結合」である。対応する要素がない場合でも、左側のすべての種類の要素を得ることができる

 方法は簡単で、「DefaultIfEmptyメソッド」をfrom句に追加するだけである(リスト15.23参照)。

var query = from x in 商品情報データ
            join y in 商品販売価格データ on x.Id equals y.Id into z
            from a in z.DefaultIfEmpty(
                    new 商品販売価格() { 店名 = "取り扱い店なし" })
            select new { Name = x.名前, 販売店 = a };
リスト15.23 左外部結合の例(クエリ式以外はリスト15.7と同様のため省略)

PC-8001
    BitOut
PC-8001
    富士山音響
MZ-80K
    富士山音響
Basic Master Level-3
    マイコンセンターROM
Basic Master Level-3
    富士山音響
COMKIT 8060
    取り扱い店なし
リスト15.23の実行結果

 DefaultIfEmptyメソッドはSystem.Linq.EnumerableクラスからIEnumerable<TSource>に対して提供される拡張メソッドである。指定されたシーケンスを受け取ってそのまま返すが、シーケンスが空の場合に限って、その型のデフォルト値または引数で指定されたデフォルト値を返す。デフォルト値は、引数なしで使えば型のデフォルト値(int型なら0、クラスならnullなど)が使われる。引数を記述すれば、それがデフォルト値になる。

 この例では、

new 商品販売価格() { 店名 = "取り扱い店なし" }

というデフォルト値を指定している。

 これにより、対応関係が存在しない要素にはこのデフォルト値が補われ、左側の要素はすべて出力される。

 ちなみに、DefaultIfEmptyメソッドの引数にデフォルト値を書き込まず、結果にnullを含めさせるのも1つの方法である。対応する要素がない場合に複雑な処理を実行する必要があるときは、nullを返して、それをチェックするほうが処理しやすいこともある。


 INDEX
  [完全版]究極のC#プログラミング
  Chapter15 LINQとクエリ式
    1.15.1 LINQの面白さ
    2.15.2 LINQとは何か?
    3.15.3 「値の集まり」に対する演算
    4.15.4 なぜLINQなのか?
    5.15.5 最も基本的なLINQ
    6.15.6 LINQの本質は列挙
    7.15.7 LINQを使ううえでの注意点
    8.15.8 クエリ結果を加工する
    9.15.9 複数のソースからクエリする
    10.15.10 条件で絞り込む
    11.15.11 一部項目のみselectする
    12.15.12 シンプルなソート
    13.15.13 クエリの接続
    14.15.14 クエリ結果のグループ化
    15.15.15 複数ソースを関連付けるjoin句
    16.15.16 from句とjoin句のパフォーマンス
    17.15.17 join句のグループ化結合
  18.15.18 join句の左外部結合
    19.15.19 単独で使うDefaultIfEmptyメソッド
    20.15.20 内部列挙を伴うfrom句の二重使用
    21.15.21 let句
    22.15.22 クエリのインスタンス化
    23.15.23 クエリ結果の個数を得る
    24.15.24 Anyメソッドと存在チェック/【C#olumn】クエリ式のデバッグテクニック
    25.15.25 まとめ/【C#olumn】LINQの難しさ/【Exercise】練習問題
 
インデックス・ページヘ  「[完全版]究極のC#プログラミング」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間