C#でコレクションを内部結合してみた
C#で、自分で定義したクラスのコレクションを、LINQを使って内部結合してみました。
目次
内部結合とは
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);
}
出力はこうなります。
表にするとこんな感じ。
(注意:秀丸、MeryのMS-DOS版やMSX版はありません。VimもMSX版はたぶんありません。EDLINはMS-DOS用のエディタです。)
左側の表のEDLINに対応するものが右側の表に無いので、出力結果にEDLINが含まれていません。
一致するものが複数ある場合は、列挙してくれるようですね。
まとめ
指定した列のデータが一致する全ての組み合わせを返してくれる。
右側のテーブルでも左側のテーブルでも、データが一致しないものは返さない。
公開日
広告
C#でデータ処理カテゴリの投稿
- C#でDataAdapterを使ってAccessのデータベースを読み書きしてみた
- C#でDataAdapterを使ってAccessのデータベースを読み書きしてみた(OleDb編)
- C#でDataGridに表示するデータを操作してみた
- C#でDataTableのカラムのデータ型を読んでみた
- C#でDataTableの自動インクリメントをしてみた
- C#でJSON形式のデータを出力してみた
- C#でLinq to objectの結果をDataTableにしてみた
- C#でSQLite3のデータベースを使ってみる(ADO.NET ファクトリーデザインパターン)
- C#でSQLiteを使ってみる(インストール)
- C#でコレクションの要素の数を調べる
- C#でコレクションを内部結合してみた
- C#でコレクションを左外部結合してみた
- C#でコレクションを昇順または降順に並び替える
- C#で渡された配列を加工して配列で返す関数について注意すること
- C#のキューを試してみた
- C#のスタックを試してみた