方法 : 複数のソースからオブジェクト コレクションにデータを設定する (LINQ)
更新 : 2008 年 7 月
この例では、さまざまなソースの型から一連の新しい型にデータをマージする方法を示します。次のコード例では、文字列と整数の配列とをマージします。ただし、2 つのデータ ソースが何であっても、同じ原則が適用されます。これは、メモリ内オブジェクト (LINQ to SQL クエリの結果、ADO.NET データセット、XML ドキュメントなど) の任意の組み合わせでも同様です。
メモ : |
---|
まだデータベース内にあるデータを含むメモリ内データやファイル システムのデータを結合しないでください。そのようなドメイン間結合を行うと、データベース クエリとその他の種類のソースにおける結合操作の定義方法の違いが原因で、未定義の結果が生じる可能性があります。また、そのような操作では、データベース内に大量のデータが存在する場合に、メモリ不足例外が発生するおそれもあります。データベースのデータをメモリ内データに結合するには、まずデータベース クエリで ToList または ToArray を呼び出してから、返されたコレクションで結合を実行します。 |
データ ファイルを作成するには
- 新しい Visual C# または Visual Basic プロジェクトを作成し、次の names.csv ファイルと scores.csv ファイルをソリューション フォルダにコピーします。詳細については「方法 : 異種ファイルのコンテンツを結合する (LINQ)」を参照してください。
使用例
次の例は、名前付きの型 Student を使用して、メモリ内にある 2 つの文字列のコレクションからマージされたデータを格納する方法を示しています。各コレクションは、.csv 形式のスプレッドシート データをシミュレートします。1 つ目の文字列のコレクションは学生の名前と ID を表し、2 つ目のコレクションは学生 ID (最初の列) と 4 つの試験の点数を表します。
Class Student
Public FirstName As String
Public LastName As String
Public ID As Integer
Public ExamScores As List(Of Integer)
End Class
Class PopulateCollections
Shared Sub Main()
' Join content from spreadsheets into a list of Student objectss.
' names.csv contains the student name
' plus an ID number. scores.csv contains the ID and a
' set of four test scores. The following query joins
' the scores to the student names by using ID as a
' matching key, and then projects the results into a new type.
Dim names As String() = System.IO.File.ReadAllLines("../../../names.csv")
Dim scores As String() = System.IO.File.ReadAllLines("../../../scores.csv")
' Name: Last[0], First[1], ID[2], Grade Level[3]
' Omelchenko, Svetlana, 111, 2
' Score: StudentID[0], Exam1[1] Exam2[2], Exam3[3], Exam4[4]
' 111, 97, 92, 81, 60
' This query joins two dissimilar spreadsheets based on common ID value.
' Multiple from clauses are used instead of a join clause
' in order to store results of id.Split.
' Note the dynamic creation of a list of ints for the
' TestScores member. We skip 1 because the first string
' in the array is the student ID, not an exam score.
Dim scoreQuery1 = From name In names _
Let n = name.Split(New Char() {","}) _
From id In scores _
Let s = id.Split(New Char() {","}) _
Where n(2) = s(0) _
Select New Student() _
With {.FirstName = n(0), .LastName = n(1), .ID = n(2), _
.ExamScores = (From scoreAsText In s Skip 1 _
Select Convert.ToInt32(scoreAsText)).ToList()}
' Optional. Store the query results for faster access
' in future queries. May be useful with very large data files.
Dim students As List(Of Student) = scoreQuery1.ToList()
' Display the list contents
' and perform a further calculation
For Each s In students
Console.WriteLine("The average score of " & s.FirstName & " " & _
s.LastName & " is " & s.ExamScores.Average())
Next
' Keep console window open in debug mode.
Console.WriteLine("Press any key to exit.")
Console.ReadKey()
End Sub
End Class
' Output:
'The average score of Adams Terry is 85.25
'The average score of Fakhouri Fadi is 92.25
'The average score of Feng Hanying is 88
'The average score of Garcia Cesar is 88.25
'The average score of Garcia Debra is 67
'The average score of Garcia Hugo is 85.75
'The average score of Mortensen Sven is 84.5
'The average score of O'Donnell Claire is 72.25
'The average score of Omelchenko Svetlana is 82.5
'The average score of Tucker Lance is 81.75
'The average score of Tucker Michael is 92
'The average score of Zabokritski Eugene is 83
class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int ID { get; set; }
public List<int> ExamScores { get; set; }
}
class PopulateCollections
{
static void Main()
{
// These data files are defined in How to: Join Content from Dissimilar Files (LINQ)
string[] names = System.IO.File.ReadAllLines(@"../../../names.csv");
string[] scores = System.IO.File.ReadAllLines(@"../../../scores.csv");
// Merge the data sources using a named type.
// var could be used instead of an explicit type.
// Note the dynamic creation of a list of ints for the
// TestScores member. We skip 1 because the first string
// in the array is the student ID, not an exam score.
IEnumerable<Student> queryNamesScores =
from name in names
let x = name.Split(',')
from score in scores
let s = score.Split(',')
where x[2] == s[0]
select new Student()
{
FirstName = x[0],
LastName = x[1],
ID = Convert.ToInt32(x[2]),
ExamScores = (from scoreAsText in s.Skip(1)
select Convert.ToInt32(scoreAsText)).
ToList()
};
// Optional. Store the newly created student objects in memory
// for faster access in future queries. Could be useful with
// very large data files.
List<Student> students = queryNamesScores.ToList();
// Display the results and perform one further calculation.
foreach (var student in students)
{
Console.WriteLine("The average score of {0} {1} is {2}.",
student.FirstName, student.LastName, student.ExamScores.Average());
}
//Keep console window open in debug mode
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Output:
The average score of Adams Terry is 85.25.
The average score of Fakhouri Fadi is 92.25.
The average score of Feng Hanying is 88.
The average score of Garcia Cesar is 88.25.
The average score of Garcia Debra is 67.
The average score of Garcia Hugo is 85.75.
The average score of Mortensen Sven is 84.5.
The average score of O'Donnell Claire is 72.25.
The average score of Omelchenko Svetlana is 82.5.
The average score of Tucker Lance is 81.75.
The average score of Tucker Michael is 92.
The average score of Zabokritski Eugene is 83.
*/
これらの例のデータ ソースは、オブジェクト初期化子で初期化されます。クエリでは、join 句を使用して名前と点数とを照合します。ID は外部キーとして使用されます。ただし、一方のソースの ID は文字列で、もう一方のソースの ID は整数です。join では等価比較が必要となるため、まず文字列から ID を抽出し、それを整数に変換する必要があります。これを達成するには、2 つの let 句を使用します。1 つ目の let 句の一時識別子 x には、元の文字列をスペースの位置で切り離して作成された 3 つの文字列の配列が格納されます。2 つ目の let 句の識別子 n には、ID 部分の文字列を整数に変換した結果が格納されます。select 句では、2 つのソースのデータを使用して新しい各 Student オブジェクトをインスタンス化するために、オブジェクト初期化子が使用されます。
クエリの結果を格納する必要がない場合は、名前付きの型よりも匿名型の方が便利です。クエリが実行されたメソッドの外部にクエリ結果を渡す場合は、名前付きの型が必要になります。次のサンプルは、前のサンプルと同じタスクを実行しますが、名前付きの型ではなく匿名型を使用します。
' This query uses an anonymous type
' Note the dynamic creation of a list of ints for the
' TestScores member. We skip 1 because the first string
' in the array is the student ID, not an exam score.
Dim scoreQuery2 = From name In names _
Let n = name.Split(New Char() {","}) _
From id In scores _
Let s = id.Split(New Char() {","}) _
Where n(2) = s(0) _
Select New With {.Last = n(0), _
.First = n(1), _
.TestScores = (From scoreAsText In s Skip 1 _
Select Convert.ToInt32(scoreAsText)).ToList()}
' Display the list contents
' and perform a further calculation
For Each s In scoreQuery2
Console.WriteLine("The average score of " & s.First & " " & s.Last & " is " & s.TestScores.Average())
Next
// Merge the data sources by using an anonymous type.
// Note the dynamic creation of a list of ints for the
// TestScores member. We skip 1 because the first string
// in the array is the student ID, not an exam score.
var queryNamesScores2 =
from name in names
let x = name.Split(',')
from score in scores
let s = score.Split(',')
where x[2] == s[0]
select new
{
First = x[0],
Last = x[1],
TestScores = (from scoreAsText in s.Skip(1)
select Convert.ToInt32(scoreAsText))
.ToList()
};
// Display the results and perform one further calculation.
foreach (var student in queryNamesScores2)
{
Console.WriteLine("The average score of {0} {1} is {2}.",
student.First, student.Last, student.TestScores.Average());
}
コードのコンパイル方法
.NET Framework Version 3.5 を対象とする Visual Studio プロジェクトを作成します。プロジェクトには、System.Core.dll への参照と、System.Linq 名前空間に対する using ディレクティブ (C#) または Imports ステートメント (Visual Basic) が既定で含まれます。
このコードをプロジェクト内にコピーします。
F5 キーを押して、プログラムをコンパイルおよび実行します。
任意のキーを押して、コンソール ウィンドウを終了します。
参照
概念
参照
オブジェクト初期化子とコレクション初期化子 (C# プログラミング ガイド)
変更履歴
日付 |
履歴 |
理由 |
---|---|---|
2008 年 7 月 |
2 番目のコード例を追加 |
コンテンツ バグ修正 |