【VB.NET】LINQ友の会【C#, C♯, C#】

■ このスレッドは過去ログ倉庫に格納されています
1デフォルトの名無しさん
垢版 |
2008/02/09(土) 23:51:34
VisualStudio2008より追加された便利で強力な機能

  統合言語クエリ (LINQ : Language Integrated Query)

ちょっと使ってみると、意外と難しいし、テクニック的にも奥が深いものです。
関数型言語にしかないような機能ラムダ式(Lambda式)などはオブジェクト指向とは一味違う機能です。
DataBaseの操作にも、Xmlの操作にも、さらにもっと単純な配列なコンテナにさえ機能する
言語共通・高汎用な統合言語クエリを皆で一緒にマターリ勉強しましょう。
質問、便利なマイテクニックの発表、いろいろやっちゃってください。
2デフォルトの名無しさん
垢版 |
2008/02/09(土) 23:52:34
>>1 もこれから使い始めます、答えられそうな質問にはどんどん答えます、無理なのは……誰かお願い。
2008/02/10(日) 00:05:21
まずは一発目
LINQってなあに、という所で、これは以下のような事ができます。
このコードは配列から4以下の値を取り出します。

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);

とてもすっきりかけます。
2008/02/10(日) 00:20:06
部分的にSQLがかけますよ、って感じか
どーなんだろ、便利か?これ
2008/02/10(日) 00:25:18
まあ、便利なんだろうけど、あまり優秀でないプログラマに使わせるとこんな記述が
そこらじゅうにばら撒かれたきわめて保守し辛いプログラムが量産されるような気が
する。
2008/02/10(日) 00:34:33
もう全部の言語合体したのを作っちゃいなよw

CLR自体に新しい何かが加わったの?
.NET層のインターフェイス?
シンタックスシュガーにも見えなくもない。
全然c#に触れてないので見当違いなこと言ってたらごめんよ。
2008/02/10(日) 00:53:14
>>6
ラムダ式のシンタックスシュガーですよ、かなりなんでも出来ます。
2008/02/10(日) 00:55:57
明日は、とりあえず難しい事考えなくても、簡単に使えるサンプルでも考えてみるかな・・・
2008/02/10(日) 01:15:38
洋書だとC#3の本がかなり出てるらしいけど、日本語のはぜんぜんでないな。
2008/02/10(日) 07:20:46
何が便利かって、グループ化の機能が便利だ。
11762
垢版 |
2008/02/10(日) 12:42:26
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で遊んでみた
2008/02/11(月) 15:15:26
データベースからデータを拾ってきて、LocationID が 10 未満の行を取り出すサンプル。
従来コードから徐々に LINQ に書き換えて行って見ました。
データは、Microsoft SQL Server 2005 に最初から入っているものです。
こちらの使用環境は Professional エデッションで、二枚付いているディスクはフルインストール
ウインドウズ認証でインストールして特に変わった設定がしてなければ、"貴方のPC" を自分のPC名に書き換えれば動くと思います、多分。
データベースは、最初から入っているサンプルデータ AdventureWorks の中の Location(Production) を使ってみました。
DataGridView コントロールを二つ貼り付けて、ボタンを一つ用意して、ボタンプッシュのイベントで実行しています。
2008/02/11(月) 15:16:32
続き(>>12)
次のように変換されます。
1 Tool Crib 0.0000 0.00 1998/06/01
2 Sheet Metal Racks 0.0000 0.00 1998/06/01
3 Paint Shop 0.0000 0.00 1998/06/01
4 Paint Storage 0.0000 0.00 1998/06/01
5 Metal Storage 0.0000 0.00 1998/06/01
6 Miscellaneous Storage 0.0000 0.00 1998/06/01
7 Finished Goods Storage 0.0000 0.00 1998/06/01
10 Frame Forming 22.5000 96.00 1998/06/01
20 Frame Welding 25.0000 108.00 1998/06/01
30 Debur and Polish 14.5000 120.00 1998/06/01
40 Paint 15.7500 120.00 1998/06/01
45 Specialized Paint 18.0000 80.00 1998/06/01
50 Subassembly 12.2500 120.00 1998/06/01
60 Final Assembly 12.2500 120.00 1998/06/01

        ↓

1 Tool Crib 0.0000 0.00 1998/06/01
2 Sheet Metal Racks 0.0000 0.00 1998/06/01
3 Paint Shop 0.0000 0.00 1998/06/01
4 Paint Storage 0.0000 0.00 1998/06/01
5 Metal Storage 0.0000 0.00 1998/06/01
6 Miscellaneous Storage 0.0000 0.00 1998/06/01
7 Finished Goods Storage 0.0000 0.00 1998/06/01
2008/02/11(月) 15:17:26
続き(>>13)
// 従来方式
private void Test4()
{
using (System.Data.SqlClient.SqlDataAdapter myDataAdapter = new System.Data.SqlClient.SqlDataAdapter("select * from Production.Location", "Data Source=貴方のPC;Initial Catalog=AdventureWorks;Integrated Security=True"))
{
// サンプルデータベースからデータを取り込む
System.Data.DataTable dataTable1 = new System.Data.DataTable();
myDataAdapter.Fill(dataTable1);
// LocationIDが10以下の行のみのテーブルを作る
System.Data.DataTable dataTable2 = dataTable1.Clone();
foreach (System.Data.DataRow tmp1 in dataTable1.Select("LocationID < 10"))
{
System.Data.DataRow tmp2 = dataTable2.NewRow();
tmp2.ItemArray = tmp1.ItemArray;
dataTable2.Rows.Add(tmp2);
}
// ためしに表示
this.dataGridView1.DataSource = dataTable1;
this.dataGridView2.DataSource = dataTable2;
}
}
2008/02/11(月) 15:18:11
続き(>>14)
// var を使ったバージョン
private void test5()
{
using (var myDataAdapter = new System.Data.SqlClient.SqlDataAdapter("select * from Production.Location", "Data Source=貴方のPC;Initial Catalog=AdventureWorks;Integrated Security=True"))
{
// サンプルデータベースからデータを取り込む
var dataTable1 = new System.Data.DataTable();
myDataAdapter.Fill(dataTable1);
// LocationIDが10以下の行のみのテーブルを作る
var dataTable2 = dataTable1.Clone();
foreach (var tmp1 in dataTable1.Select("LocationID < 10"))
{
var tmp2 = dataTable2.NewRow();
tmp2.ItemArray = tmp1.ItemArray;
dataTable2.Rows.Add(tmp2);
}
// ためしに表示
this.dataGridView1.DataSource = dataTable1;
this.dataGridView2.DataSource = dataTable2;
}
}
2008/02/11(月) 15:19:25
続き(>>15)
// LINQ を使ったバージョン(意味が解りやすいように型付き)
private void Test7()
{
using (System.Data.SqlClient.SqlDataAdapter myDataAdapter = new System.Data.SqlClient.SqlDataAdapter("select * from Production.Location", "Data Source=貴方のPC;Initial Catalog=AdventureWorks;Integrated Security=True"))
{
// サンプルデータベースからデータを取り込む
System.Data.DataTable dataTable1 = new System.Data.DataTable();
myDataAdapter.Fill(dataTable1);
// LocationIDが10以下の行のみのテーブルを作る
EnumerableRowCollection<System.Data.DataRow> query = from tmp2 in dataTable1.AsEnumerable() where tmp2.Field<short>("LocationID") < 10 select tmp2;
System.Data.DataTable dataTable2 = query.CopyToDataTable();
// ためしに表示
this.dataGridView1.DataSource = dataTable1;
this.dataGridView2.DataSource = dataTable2;
}
}
17デフォルトの名無しさん
垢版 |
2008/02/11(月) 15:25:03
続き(>>16) 最後
// LINQ を使ったバージョン
private void Test8()
{
using (var myDataAdapter = new System.Data.SqlClient.SqlDataAdapter("select * from Production.Location", "Data Source=貴方のPC;Initial Catalog=AdventureWorks;Integrated Security=True"))
{
// サンプルデータベースからデータを取り込む
var dataTable1 = new System.Data.DataTable();
myDataAdapter.Fill(dataTable1);
// LocationIDが10以下の行のみのテーブルを作る
var tmp1 = from tmp2 in dataTable1.AsEnumerable() where tmp2.Field<short>("LocationID") < 10 select tmp2;
var dataTable2 = tmp1.CopyToDataTable();
// ためしに表示
this.dataGridView1.DataSource = dataTable1;
this.dataGridView2.DataSource = dataTable2;
}
}
2008/02/11(月) 15:28:13
さて、次はLINQ to Sql にいくか、それともしょうもない内容にすべか
誰も来なければそろそろ諦めるかw
2008/02/11(月) 17:33:56
LINQ: .NET 統合言語クエリ
http://www.microsoft.com/japan/msdn/net/bb308959.aspx

Part4 LINQで変わるデータベース開発:ITpro
http://itpro.nikkeibp.co.jp/article/COLUMN/20080116/291140/?P=1&ST=develop


スレ立てるの早すぎだと思われ
然程使ってもいないのに意見の交換なんて出来ませぬ
2008/02/11(月) 18:16:46
>>1-17
1行に詰め込まないでインデントしようよ。
2008/02/11(月) 18:58:15
最後の一つだけインデント入れてみた
private void Test8() {
 using (var myDataAdapter = new System.Data.SqlClient.SqlDataAdapter(
  "select * from Production.Location",
  "Data Source=貴方のPC;Initial Catalog=AdventureWorks;Integrated Security=True"
  )
 ) {
  // サンプルデータベースからデータを取り込む
  var dataTable1 = new System.Data.DataTable();
  myDataAdapter.Fill(dataTable1);
  // LocationIDが10以下の行のみのテーブルを作る
  var srcTable = dataTable1.AsEnumerable() ;
  var dstTable =
    from row in srcTable
    where row.Field<short>("LocationID") < 10
    select row;
  // ためしに表示
  dataGridView1.DataSource = dataTable1;
  dataGridView2.DataSource = dstTable.CopyToDataTable();
 }
}
2008/02/11(月) 19:11:14
どんどんコードが短くなる気持のよいビデオです
http://www.microsoft.com/uk/msdn/nuggets/nugget/227/Decomposing-LINQ.aspx
2008/02/12(火) 16:11:50
続き(>>17)
今日は System.Data.Linq.DataContext を使ったさらなる単純な例。
データベースへのアクセスの高速化もやりたいと思ったんですが、そんなの興味無い人多いと思うので、思いとどまって簡単化の方のみとします。
まずテーブルと同じ形式を持つクラスを作成します。
中身の無い { get; set; } は、event に対するdelegateのように、自動的にその型のインスタンスを生成してアクセスできるようにする物です。
C#の言語仕様書を見てください。
2008/02/12(火) 16:12:29
続き(>>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; }
}
2008/02/12(火) 16:13:13
続き(>>24)
private void Test9()
{
// DataContext を使ったバージョン
using (var myDataContext = new System.Data.Linq.DataContext("Data Source=貴方のPC;Initial Catalog=AdventureWorks;Integrated Security=True"))
{
// サンプルデータベースからデータを取り込む
System.Data.Linq.Table<TableLocation> orginalTable = myDataContext.GetTable<TableLocation>();

// LocationIDが10以下の行のみのテーブルを作る
var lessThan10Table = from row in orginalTable where row.LocationID < 10 select row;

// ためしに表示
this.dataGridView1.DataSource = orginalTable;
this.dataGridView2.DataSource = lessThan10Table;

// *** 重要な特徴の紹介 ***
// キャストしなくても使えるのでミスが少なく便利である
foreach (TableLocation tmp in lessThan10Table)
{
System.Console.WriteLine("{0},{1},{2},{3},{4}"
, tmp.LocationID
, tmp.Name
, tmp.CostRate
, tmp.Availability
, tmp.ModifiedDate);
}
}
}
26デフォルトの名無しさん
垢版 |
2008/02/12(火) 16:14:20
今日はこれでおしまい
データベースを知らない人にも解りやすい、データベース不使用例もそのうち作ってみます。
2008/02/12(火) 21:46:14
「プロジェクト」の「新しい項目の追加」で「LINQ to SQL クラス」のテンプレートが
追加されるけど知らない人多いよね。これで>>24のを自動生成してくれる。
28デフォルトの名無しさん
垢版 |
2008/02/13(水) 13:39:40
ども、>>1です、まだ始めたばかりで右も左もわかりませんがよろしくお願いします。
驚きました、プロジェクトから Linq To Sql を作成して、サーバーエクスプローラで必要なデータベースを接続したら、テーブルをドラックアンドドロップでポイだったんですね。
ちょっちょっと、これは何にもする事が無いです、いきなりこのスレ終了ですかぁ(^^;
せっかく建てたのだから何かネタ考えて続けて見ます・・・

private void Test10()
{
using (var dataContext = new TestTableDataContext())
{
var table = dataContext.GetTable<Location>();
this.dataGridView1.DataSource = table;
this.dataGridView2.DataSource = from row in table where row.LocationID < 10 select row;
}
}
2008/02/13(水) 13:49:37
このデザイナは、細かい所まで完全に実装してくれますね
自前で動的にテーブルやDBを作成するにしても、一旦SqlSever上でダミーテーブルを作って
コードを生成しておいて、それをコピペで取ってきて変更する方が効率が良いように思えてきました。
2008/02/13(水) 14:02:30
DB板のもあんまり延びてないね
http://pc11.2ch.net/test/read.cgi/db/1173880885/l50
2008/02/13(水) 14:09:09
DB板は、あっちはアドミニの人ばかりです。
マの人がほとんど居ないから余計伸びないです。
プログラム的な事は聞いても答えが返ってくることまずないですし。
2008/02/15(金) 00:07:33
検索してからC#オブジェクトに変換する手間がとても万度くさい自分はLINQヽ(´ー`)ノマンセーです。
遅くても(・ε・)キニシナイ!!。
33デフォルトの名無しさん
垢版 |
2008/02/15(金) 03:30:14
IronLisp#早く作れよ
2008/02/15(金) 08:59:25
ググれカス
2008/02/15(金) 10:20:31
DBを使わなくてもLINQは便利なんだよ〜!ってことを布教するためw、
普通のコレクションに対してロジックを実行するクエリのサンプルを示してみます。
>>10が言うように、グループ化の処理をクエリ一発で書けるのは実に魅力的です。

ファイル名を格納したstring配列 files に対して処理を行います。

// 拡張子ごとにファイル名をグループ化するクエリ
var extGroupQuery = from filename in files
          let ext = System.IO.Path.GetExtension(filename).ToLower()
          group filename by ext into extGroup
          select extGroup;

// グループ化したクエリは2重のIEnumerable<T>になってます
foreach (var extGroup in extGroupQuery)
{
  Console.WriteLine("Extension: {0}, Count: {1}", extGroup.Key, extGroup.Count());

  foreach (var filename in extGroup)
  {
    Console.WriteLine("\t{0}", filename);
  }
}
3635
垢版 |
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() };
3735
垢版 |
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 };

とりあえず以上です。
もっと気の利いた例を作ったら、また書き込みます。
3835
垢版 |
2008/02/15(金) 11:52:15
>>35-37の応用で、ファイルの重複を検出するコードをLINQでコンパクトに書いてみます。
次のメソッドが存在するという前提です。

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();
  }
}
3935
垢版 |
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 hashGroup in hashGroupQuery)
{
  Console.WriteLine("Hash: {0}, Count: {1}", hashGroup.Key, hashGroup.Count());

  foreach (var filehash in hashGroup)
  {
    Console.WriteLine("\t{0}", filehash.Filename);
  }
}
4035
垢版 |
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;

  foreach (var hashGroup in hashGroupQuery2)
  {
    // (行数オーバーのため略 >>39と同じです)
  }
}
41デフォルトの名無しさん
垢版 |
2008/02/16(土) 00:03:52
>>35
おお >>1 です、いらっしゃいまし、どんどん布教してあげてください。
let とか小技聞いていて面白いですね。
私の方は、まだ初めて一週間と初心者なので、もっと基本的でつまらない物やっていきます。

今日は、下手をすれば、C#さえ初心者の人向けのQ&Aを作ってみようと思います。
結果の簡単な確認方法と、選択・ソートです。
明日以後、表の結合等をやってみます、そのあとは、ググればありそうなのに無いLINQの文法の詳細解説あたりをやってみます。
2008/02/16(土) 00:04:35
Q.データを確認したい
 ツールバーにある DataGridView は、とりあえずデータの中身を見てみたい時にとても便利です。
 DataSource プロパティーにデータをセットする事で見ることができます。
 ただし、ここに設定できるオブジェクトは IList , IListSource , IBindingList , IBindingListView
 の四つです。
 また、要素は、フィールドメンバでは駄目で、プロパティーでなければなりません。
 C# 3.0 より追加された省略形が便利です。
 class Row {
  public int Data1 { set ; get ; }
  public int Data2 { set ; get ; }
 }
 dataGridView1.DataSource = new Row[] { new Row() { Data1 = 1, Data2 = 2 } };
2008/02/16(土) 00:05:16
Q.from ... として作ったデータが見れない
 IList 等に変換してしまうのが便利です。
 
 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();
2008/02/16(土) 00:05:53
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();
2008/02/16(土) 00:06:24
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();
2008/02/16(土) 00:15:04
 2.二つ以上の項目についてソートする場合は以下の通りです。
 class Row {
  public string S { set; get; }
  public int D { set; get; }
 }
 var tableOrg = new Row[] {
  new Row() { S="A" , D=3 },
  new Row() { S="B" , D=1 },
  new Row() { S="A" , D=4 },
  new Row() { S="B" , D=1 },
  new Row() { S="A" , D=5 },
  new Row() { S="B" , D=9 },
 };
 // SとD を使って比較します
 var table1 = from row in tableOrg orderby row.S, row.D select row;
 dataGridView1.DataSource = table1.ToArray();
2008/02/16(土) 00:15:29
 3.二回に分けてソートする場合は以下の通りです。(2と結果が異なります)
 class Row {
  public string S { set; get; }
  public int D { set; get; }
 }
 var tableOrg = new Row[] {
  new Row() { S="A" , D=3 },
  new Row() { S="B" , D=1 },
  new Row() { S="A" , D=4 },
  new Row() { S="B" , D=1 },
  new Row() { S="A" , D=5 },
  new Row() { S="B" , D=9 },
 };
 // SとD を使って比較します
 var table1 = from row in tableOrg orderby row.S orderby row.D select row;
 dataGridView1.DataSource = table1.ToArray();
2008/02/16(土) 09:02:20
ところで、LINQってどう発音するの?
2008/02/16(土) 09:48:38
発音は link と同じらしいよ。
2008/02/16(土) 17:51:35
>>1 です、今日は、表の結合のやり方を紹介します。
2008/02/16(土) 17:52:36
Q.表を結合したい1(クロス結合)
 Row1の表にRow2の表の要素をすべて結合します。
 
 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 の結果
 赤 青
 赤 緑
 青 青
 青 緑
この機能、一見は使い道がなさそうですが、これをフィルターしてゆく事により価値が出てきます。
2008/02/16(土) 17:53:19
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
2008/02/16(土) 17:54:00
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
54デフォルトの名無しさん
垢版 |
2008/02/16(土) 17:54:50
そのうち、逆引きLINQでも作って、纏めてみたいな……
こうやって、Q&A を作ってみると、LINQは集合の積的な演算は強いのに和の方は至って貧弱なんですね。
全部LINQにしなくても適当にメソッドを使えば良いという話もありますが……
2008/02/16(土) 18:39:49
>>54
まあ、リレーショナルデータベースがそういうものだからね<テーブル積を多用
2008/02/16(土) 18:59:05
>>55
他にも同値分割とか余りとか(袋の中身を色で割ると、それぞれの色で分類された集合と、色なし集合になるといった概念です)、まだまだ綺麗にできる所があるなと >>1 は感じてます
マイクロソフトリサーチの人たちに、ぜひ圏論(この種の概念の理論がいっぱいあります)の研究をしてみてもらいたいなと……
そしてこの恐ろしげな概念を誰の手にも簡単に使える直感的で便利機能に仕上げて欲しいものです。
2008/02/16(土) 19:11:27
そういう概念を導入すると、
作り手だけでなくて、使い手にも圏論の知識を強要することにならないかな。

LINQ は、リレーショナルデータベースの文化を
OOP 言語に導入したものだから、
少なくとも SQL チックに select とか where ってキーワードを使ってる限り、
リレーショナルデータベースの文化を大きく逸脱するようなことは
しない方がいいと思う。

ヘジたんがクエリ式否定派(関数形式ばっかり使う)な理由も
そういうところにあるのかもね。
2008/02/16(土) 19:11:51
>>53
ちょっとだけ短くしたよ。

var table1 = from row1 in tableOrg1
        let 評価 = tableOrg2.First(row2 => row2.以上 <= row1.得点 && row1.得点 < row2.未満)
        select new { row1.氏名, 評価.評価 };
■ このスレッドは過去ログ倉庫に格納されています
5ちゃんねるの広告が気に入らない場合は、こちらをクリックしてください。

ニューススポーツなんでも実況