C#でコレクションを内部結合してみた

C#で、自分で定義したクラスのコレクションを、LINQを使って内部結合してみました。

目次

  1. 内部結合とは
  2. 試してみた
    1. 多対1の場合
    2. 多対多の場合
  3. まとめ

内部結合とは

2つのテーブルの列の値が同じレコードを取得するものです。内部結合とかinner joinとかでググると、たくさん説明が出てきます。

LINQを使うと、コレクションに対して内部結合ができます。

コレクション(A)とコレクション(B)の内部結合をする場合のクエリは、こんな感じです。

var query = from 変数名(A) in コレクション(A)
            join 変数名(B) in コレクション(B)
            on 変数名(A).比較するプロパティ equals 変数名(B).比較するプロパティ
            select new {プロパティ = 変数名(A).プロパティ}

それぞれのコレクションから取得したいプロパティを、new{}の中にカンマ区切りで列挙すれば良いわけですね。

あとはforeachでqueryを実行します。

試してみた

多対1の場合

この2つのコレクションを、os_idとidをキーにして内部結合してみます。

データ

上記のテーブルは、それぞれCSV形式で別々のファイルに保存しておきます。CSVファイルの読み込みには、 TextFieldParser を使います。

コードはこんな感じです。

using System;
using System.Linq;
using System.Collections.ObjectModel;
using Microsoft.VisualBasic.FileIO;

namespace trial_linq
{
    class Program
    {
        static void Main(string[] args)
        {
            // 結合するコレクション
            Collection<RelationEditorOs> RelationTable = new Collection<RelationEditorOs>();
            Collection<OSList> OSLists = new Collection<OSList>();

            // 1つめのテーブルの読み込み
            TextFieldParser parser = new TextFieldParser("relation_editor_os.csv", System.Text.Encoding.GetEncoding("Shift_JIS"));
            using (parser)
            {
                parser.TextFieldType = FieldType.Delimited;
                parser.SetDelimiters(",");
                parser.CommentTokens = new string[] { "#" };
                parser.HasFieldsEnclosedInQuotes = true;
                parser.TrimWhiteSpace = true;

                while (!parser.EndOfData)
                {
                    string[] row = parser.ReadFields();
                    RelationEditorOs rel = new RelationEditorOs();
                    rel.ID = int.Parse(row[0]);
                    rel.EditorName = row[1];
                    rel.EditorID = int.Parse(row[2]);
                    rel.OSID = int.Parse(row[3]);
                    RelationTable.Add(rel);
                }
            }

            // 2つめのテーブルの読み込み
            TextFieldParser parser2 = new TextFieldParser("os_table.csv", System.Text.Encoding.GetEncoding("Shift_JIS"));
            using (parser)
            {
                parser2.TextFieldType = FieldType.Delimited;
                parser2.SetDelimiters(",");
                parser2.CommentTokens = new string[] { "#" };
                parser2.HasFieldsEnclosedInQuotes = true;
                parser2.TrimWhiteSpace = true;

                while (!parser2.EndOfData)
                {
                    string[] row = parser2.ReadFields();
                    OSList rel = new OSList();
                    rel.ID = int.Parse(row[0]);
                    rel.OsName = row[1];
                    rel.IssureID = int.Parse(row[2]);
                    OSLists.Add(rel);
                }
            }

            // クエリ
            var query = from p in RelationTable
                        join q in OSLists
                        on p.OSID equals q.ID
                        select new { EditorName = p.EditorName, OSID = p.OSID, OSTableID = q.ID, OSName = q.OsName };

            // クエリの実行と出力
            Console.WriteLine("Editor\tOS ID\tID\tOS Name");
            foreach (var s in query)
            {
                string str = string.Empty;
                str = s.EditorName + "\t" + s.OSID + "\t" + s.OSTableID + "\t" + s.OSName;
                Console.WriteLine(str);
            }

        }

        public class RelationEditorOs
        {
            public int ID { get; set; }
            public string EditorName { get; set; }
            public int EditorID { get; set; }
            public int OSID { get; set; }
        }

        public class OSList
        {
            public int ID { get; set; }
            public string OsName { get; set; }
            public int IssureID { get; set; }
        }
    }
}

ファイルの読み込み部分の方が長いですね。

出力結果はこうなりました。

出力結果

表にするとこんな感じ。

結果の表

一致するものだけを取り出すので、右側の表のMS-DOSが出力結果にありません。

多対多の場合

右側のテーブルのキーが重複していたらどうなるでしょうか。

os_idとissure_idをキーにして内部結合してみます。

コードはクエリと出力の部分だけを変更しましたので、その部分だけ抜粋します。

// クエリ
var query = from p in RelationTable
            join q in OSLists
            on p.OSID equals q.IssureID
            select new { EditorName = p.EditorName, OSID = p.OSID,    OSTableID = q.IssureID, OSName = q.OsName };

// クエリの実行と出力
Console.WriteLine("Editor\tOS ID\tIssure\tOS Name");
foreach (var s in query)
{
    string str = string.Empty;
    str = s.EditorName + "\t" + s.OSID + "\t" + s.OSTableID + "\t" + s.OSName;
    Console.WriteLine(str);
}

出力はこうなります。

出力2

表にするとこんな感じ。

結果の表2

(注意:秀丸、MeryのMS-DOS版やMSX版はありません。VimもMSX版はたぶんありません。EDLINはMS-DOS用のエディタです。)

左側の表のEDLINに対応するものが右側の表に無いので、出力結果にEDLINが含まれていません。

一致するものが複数ある場合は、列挙してくれるようですね。

まとめ

  • 指定した列のデータが一致する全ての組み合わせを返してくれる。

  • 右側のテーブルでも左側のテーブルでも、データが一致しないものは返さない。

公開日

広告