int [] data = new int[] { 3, 1, 4, 1, 5, 9, 2, 6 }; IEnumerable<int> x = from s in data where s <= 4 select s; foreach (int ite in x) System.Console.Write( "{0}," , ite); 3,1,4,1,2,
これを、ちょっと短くしてみる。 int [] data = new int[] { 3, 1, 4, 1, 5, 9, 2, 6 }; foreach (int ite in from s in data where s <= 4 select s) System.Console.Write( "{0}," , ite);
static T Multiply<T>(T left, T right) { var r = Expression.Parameter(typeof(T), "left"); var l = Expression.Parameter(typeof(T), "right"); return Expression.Lambda<Func<T, T, T>>(Expression.Multiply(r, l), l, r).Compile()(left, right); } Expression Treeで遊んでみた
続き(>>23) // Name の後ろにはテーブルの名前 [System.Data.Linq.Mapping.Table(Name = "Production.Location")] public class TableLocation { // 重要:項目名はデータベースのコラム名とあわせておく // ここでは、さらにプライマリキーとして認識させる [System.Data.Linq.Mapping.Column(IsPrimaryKey = true)] public short LocationID { get; set; }
[System.Data.Linq.Mapping.Column] public string Name { get; set; }
[System.Data.Linq.Mapping.Column] public System.Decimal CostRate { get; set; }
[System.Data.Linq.Mapping.Column] public System.Decimal Availability { get; set; }
[System.Data.Linq.Mapping.Column] public System.DateTime ModifiedDate { get; set; } }
// 拡張子ごとにファイル名をグループ化するクエリ var extGroupQuery = from filename in files let ext = System.IO.Path.GetExtension(filename).ToLower() group filename by ext into extGroup select extGroup;
foreach (var filename in extGroup) { Console.WriteLine("\t{0}", filename); } }
36 名前:35 mailto:sage [2008/02/15(金) 10:24:47 ]
さらに集計処理を行います クエリの中でオブジェクトを生成し、集計処理に利用したりできます
// 拡張子ごとの合計サイズを取得する(foreachを使うとこんな感じ) Dictionary<string, long> sizeDic = new Dictionary<string, long>(); foreach (var extGroup in extGroupQuery) { var sizeQuery1 = from filename in extGroup let fileinfo = new System.IO.FileInfo(filename) select fileinfo.Length; long totalSize = sizeQuery1.Sum();
sizeDic[extGroup.Key] = totalSize; }
// 拡張子ごとの合計サイズを取得する(サブクエリを使う) // クエリ内でさらにクエリを定義できます(totalSizeQueryはsizeQuery1とまったく同じです) var sizeQuery2 = from extGroup in extGroupQuery let totalSizeQuery = (from filename in extGroup let fileinfo = new System.IO.FileInfo(filename) select fileinfo.Length) select new { Extension = extGroup.Key, TotalSize = totalSizeQuery.Sum() };
37 名前:35 mailto:sage [2008/02/15(金) 10:26:11 ]
// .Sum()の場所を変えてみる(結果はsizeQuery2と同じです) var sizeQuery3 = from extGroup in extGroupQuery let totalSize = (from filename in extGroup let fileinfo = new System.IO.FileInfo(filename) select fileinfo.Length).Sum() select new { Extension = extGroup.Key, TotalSize = totalSize };
// サブクエリを拡張メソッドに変えてみる(場合によってはサブクエリよりもすっきり書けます) var sizeQuery4 = from extGroup in extGroupQuery let totalSize = extGroup.Select<string, long>(filename => new System.IO.FileInfo(filename).Length).Sum() select new { Extension = extGroup.Key, TotalSize = totalSize };
// extGroupQuery, sizeQuery2をまとめてみる var sizeQuery5 = from filename in files let ext = System.IO.Path.GetExtension(filename).ToLower() group filename by ext into extGroup let totalSizeQuery = (from filename in extGroup let fileinfo = new System.IO.FileInfo(filename) select fileinfo.Length) select new { Extension = extGroup.Key, TotalSize = totalSizeQuery };
static string GetMD5String(string filename) { using (System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider()) { byte[] data = System.IO.File.ReadAllBytes(filename); byte[] hash = md5.ComputeHash(data);
StringBuilder buf = new StringBuilder(); foreach (byte b in hash) { buf.AppendFormat("{0:x2}", b); }
return buf.ToString(); } }
39 名前:35 mailto:sage [2008/02/15(金) 11:52:51 ]
まずは単純だけど遅いバージョンから
// ファイルをハッシュ値でグループ化する // 全ファイルを読み込んでハッシュ値を計算するので遅いです var hashGroupQuery = from filehash in (from filename in files let hash = GetMD5String(filename) select new { Filename = filename, Hash = hash }) group filehash by filehash.Hash into hashGroup where hashGroup.Count() >= 2 select hashGroup;
foreach (var filehash in hashGroup) { Console.WriteLine("\t{0}", filehash.Filename); } }
40 名前:35 mailto:sage [2008/02/15(金) 11:56:08 ]
次に前処理を行って高速化したバージョン
// 処理を高速化するため、ファイルサイズでグループ化しておく // (ファイルサイズはファイルを読み込まなくても取得できるので) var sizeGroupQuery = from filesize in (from filename in files let fileinfo = new System.IO.FileInfo(filename) let size = fileinfo.Length select new { Filename = filename, Size = size }) group filesize by filesize.Size into sizeGroup where sizeGroup.Count() >= 2 select sizeGroup;
foreach (var sizeGroup in sizeGroupQuery) { // ハッシュ値でグループ化する var hashGroupQuery2 = from filehash in (from filesize in sizeGroup let hash = GetMD5String(filesize.Filename) select new { Filename = filesize.Filename, Hash = hash }) group filehash by filehash.Hash into hashGroup where hashGroup.Count() >= 2 select hashGroup;
var tableX = new [] { new { X=1 , P=0.25 }, new { X=2 , P=0.50 }, new { X=3 , P=0.25 }, }; var table2 = from row in tableX orderby row.P select row; dataGridView1.DataSource = table2.ToArray();
Q.表の中から必要な要素を選択したい where句を使います。 class Row { public int X { set ; get ; } public double P { set ; get ; } } var tableX = new Row[] { new Row() { X=1 , P=0.25 }, new Row() { X=2 , P=0.50 }, new Row() { X=3 , P=0.25 }, }; // Xが2以下の行を選択する var table1 = from row in tableX where row.X <= 2 select row; // Xが2以下の行を選択したのち、さらに P <= 0.25 となる行を絞り込む var table2 = from row in tableX where row.X <= 2 where row.P <= 0.25 select row; dataGridView1.DataSource = table1.ToArray(); dataGridView2.DataSource = table2.ToArray();
Q.表をソートしたい 1.orderby句を使います、単純なソートは以下の通りです。 class Row { public string S { set; get; } public int D { set; get; } } var tableOrg = new Row[] { new Row() { D=3 }, new Row() { D=1 }, new Row() { D=4 }, new Row() { D=1 }, new Row() { D=5 }, new Row() { D=9 }, }; // 昇順(ascending省略可) var table1 = from row in tableOrg orderby row.D ascending select row; // 降順 var table2 = from row in tableOrg orderby row.D descending select row; dataGridView1.DataSource = table1.ToArray(); dataGridView2.DataSource = table2.ToArray();
class Row1 { public string S { set; get; } } class Row2 { public string S { set; get; } } class Row3 { public string R1 { set; get; } public string R2 { set; get; } }
var tableOrg1 = new Row1[] { new Row1() { S="赤" }, new Row1() { S="青" },}; var tableOrg2 = new Row2[] { new Row2() { S="青" }, new Row2() { S="緑" },}; var table1 = from row1 in tableOrg1 from row2 in tableOrg2 select new Row3() { R1 = row1.S, R2 = row2.S }; dataGridView1.DataSource = table1.ToArray(); dataGridView2.DataSource = table2.ToArray(); table1 の結果 赤 青 赤 緑 青 青 青 緑 この機能、一見は使い道がなさそうですが、これをフィルターしてゆく事により価値が出てきます。
Q.表を結合したい2(等価結合、きっと良く使うに違いない) Row1の表にRow2の表の要素を普通に結合します。 この例では、 tableOrg1 の Index 番号と同じ Index のある tableOrg2 の列に結合します。 class Row1 { public int LineNo { set; get; } public int Index { set; get; } } class Row2 { public int Index { set; get; } public string Data1 { set; get; } public string Data2 { set; get; } } class Row3 { public int LineNo { set; get; } public string Data1 { set; get; } public string Data2 { set; get; } }
var tableOrg1 = new Row1[] { new Row1() { LineNo=1 , Index = 3 }, new Row1() { LineNo=2 , Index = 1 }, new Row1() { LineNo=3 , Index = 4 }, new Row1() { LineNo=4 , Index = 1 }, new Row1() { LineNo=5 , Index = 5 }, }; var tableOrg2 = new Row2[] { new Row2() { Index=1 , Data1 = "Index1" , Data2="ABC" }, new Row2() { Index=2 , Data1 = "Index2" , Data2="DEF" }, new Row2() { Index=3 , Data1 = "Index3" , Data2="GHI" }, new Row2() { Index=4 , Data1 = "Index4" , Data2="JKL" }, new Row2() { Index=5 , Data1 = "Index5" , Data2="MNL" }, }; var table1 = from row1 in tableOrg1 join row2 in tableOrg2 on row1.Index equals row2.Index select new Row3() { LineNo = row1.LineNo, Data1 = row2.Data1, Data2 = row2.Data2 }; table1 の結果 1 Index3 GHI 2 Index1 ABC 3 Index4 JKL 4 Index1 ABC 5 Index5 MNL
Q.表を結合したい3(非等価結合、やや使う違いない) let を使って、選択した行を保持する事により実現できます。 class 得点表 { public string 氏名 { set; get; } public int 得点 { set; get; } } class 評価表 { public int 以上 { set; get; } public int 未満 { set; get; } public string 評価 { set; get; } } class 氏名と評価 { public string 氏名 { set; get; } public string 評価 { set; get; } }
var tableOrg1 = new 得点表[] { new 得点表() { 氏名="Aさん" , 得点=10 }, new 得点表() { 氏名="Bさん" , 得点=100 }, new 得点表() { 氏名="Cさん" , 得点=60 }, }; var tableOrg2 = new 評価表[] { new 評価表() { 以上=0 , 未満=20, 評価="丙" }, new 評価表() { 以上=20 , 未満=70, 評価="乙" }, new 評価表() { 以上=70 , 未満=101, 評価="甲" }, }; var table1 = from row1 in tableOrg1 let selectLines = from row2 in tableOrg2 where (row2.以上 <= row1.得点 && row1.得点 < row2.未満) select row2 select new 氏名と評価() { 氏名 = row1.氏名, 評価 = selectLines.First().評価 }; table1 の結果 Aさん 丙 Bさん 甲 Cさん 乙 あんま綺麗じゃない……equalsに対してLINQの拡張求む > Microsoft
var table1 = from row1 in tableOrg1 let 評価結果 = tableOrg2.Where(row2 => row2.以上 <= row1.得点 && row1.得点 < row2.未満) .Select(row2 => row2.評価) .Concat(Enumerable.Repeat("見つからないよ", 1)) .Take(1) from 評価 in 評価結果 select new { row1.氏名, 評価 };
Q.表を分割したい LINQの文法は、例えば以下のような形式になっています。 from アイテム名 in 元データ where アイテム名.項目=="条件のあっているデータ" select 新しい行の定義 上記の文の最後の部分「select 新しい行の定義」の部分を「group 新しい行の定義 by ふるい分けの基準となる値」 とする事により表を分割できます。
Q.特定項目で分類したい 項目に大小比較ができれば、ソートしてしまえば良いのですが、それが無い場合には少し困った事になります。 ここでは、前出の「表を分割したい」と組み合わせた方法を紹介します。 それは、前出のLINQ文「from row in tableOrg1 group row by row.Data」の後ろに「into tmp1 from tmp2 in tmp1 select tmp2」を追加して from row in tableOrg1 group row by row.Data into tmp1 from tmp2 in tmp1 select tmp2 とすると実現できます、まじないの部類ですが・・・うまく行きます。
var tableOrg1 = new[] { new { LineNo = 1 , Data = "ABC" } , new { LineNo = 2 , Data = "DEF" } , new { LineNo = 3 , Data = "ABC" } , new { LineNo = 4 , Data = "DEF" } , new { LineNo = 5 , Data = "GHI" } , }; var table1 = from row in tableOrg1 group row by row.Data into tmp1 from tmp2 in tmp1 select tmp2; dataGridView1.DataSource = table1.ToArray();