【C# LINQ(リンク)】2つのListから完全外部結合を返すクエリ式を組んでみた

スポンサーリンク
プログラミング

業務で2つのCSVファイルの比較ファイルを手作業で作成していた時、先輩プログラマの方から、

そんなの、ツール作ってボタン一つで作れるようにしちゃえよ。

と軽く言われました。そこで、C#のLINQを使い、イイ感じに出来ないかと思い、やってみたところ、少しハマってしまったので、その備忘録です。

今回C#のLINQでやりたいこと

今回やろうとしたことは以下の通りです。

2つのListから差分を返す

前年度と今年度の2つの社員リストがあり、前年度と今年度の社員リストを並べて、退職した社員と入社した社員に対応する社員が存在しない場合は空欄にする必要がありました。

外部結合について

泥臭く比較処理を組んでいく方法も考えましたが、もっとスマートに出来ないか、色々調べてみると、SQLの考え方で「外部結合」というものがありました。左外部結合、右外部結合、完全外部結合の3種類あるようですが、今回やりたいことは完全外部結合に当たることが分かりました。(それぞれの説明はこちらのサイトに分かりやすく書かれているので、参考にしてみてください。)

LINQに完全外部結合がない

C#の優れたデータ処理ライブラリ「LINQ」なら2つのリストから完全外部結合を返すメソッドがあるだろうと考え、公式ドキュメントを調べましたが、奇しくも見つけたのは悲報でした。

LINQ を使用して FULL OUTER JOIN 構造を実装する方法は?
完全な外部結合の場合、現在、左右の外部結合を実装する DefaultIfEmpty() のような単純なメソッドはありません。

Microsoft コミュニティより引用

しかし、代替手段として以下のようなコードも紹介されていました。

var leftoutrtjoin = (from order in db.Orders
                     join team in db.Teams on order.OrderID equals team.TeamID into ot
                     from otnew in ot.DefaultIfEmpty()
                     select new { OrderID = order.OrderID, TeamID = otnew == null ? 0 : otnew.TeamID }).ToList();

var rightouterjoin = (from team in db.Teams
                      join order in db.Orders on team.TeamID equals order.OrderID into ot
                      from otnew in ot.DefaultIfEmpty()
                      select new { OrderID = otnew == null ? 0 : otnew.OrderID, TeamID = team.TeamID }).ToList();

var fullouterjoin = leftoutrtjoin.Concat(rightouterjoin);

この例ではIDを基準に完全外部結合していますが、私の場合、4カラムのデータそれぞれで存在チェックしたかったので、以下のように書き換えました。

LINQの左外部結合と右外部結合をConcatして完全外部結合する

出来上がったクエリ式はこんな感じです。

//2つのリスト
List<string> sLeftList = DB.exam1.ToList();  //左側のリスト
List<string> sRightList = DB.exam2.ToList(); //右側のリスト

//左外部結合
var leftouterjoin = (from left in sLeftList
                     join right in sRightList
                     on new { ary0 = left[0], ary1 = left[1], ary2 = left[2], ary3 = left[3] } 
                     equals new { ary0 = right[0], ary1 = right[1], ary2 = right[2], ary3 = right[3] } into rl
                     from rlnew in rl.DefaultIfEmpty()
                     select new { Left = left, Right= rlnew }).ToList();

//右外部結合
var rightouterjoin = (from right in sRightList
                      join left in sLeftList
                      on new { ary0 = right[0], ary1 = right[1], ary2 = right[2], ary3 = right[3] }
                      equals new { ary0 = left[0], ary1 = left[1], ary2 = left[2], ary3 = left[3] } into rl
                      from rlnew in rl.DefaultIfEmpty()
                      select new { Left = rlnew, Right = right }).ToList();

//完全外部結合
var fullouterjoin = leftouterjoin.Concat(rightouterjoin).Distinct().ToList();

以上です。ここまでお読みいただきありがとうございました。まだまだ勉強中なので、誤りがありましたらコメントで指摘いただけると幸いです。

コメント

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