2011-06-02 10 views
0

ソートしてから任意のサイズのバッチ(たとえば、バッチごとに最大300レコード)にグループ化する必要があるレコードのCSVファイルがあります。各バッチの内容は(2つの異なる列の内容に基づいて)均等でなければならないので、各バッチは300レコード未満である可能性があります。複数のGroupByを持つバッチ

var query = (from line in EbrRecords 
      let EbrData = line.Split('\t') 
      let Location = EbrData[7] 
      let RepName = EbrData[4] 
      let AccountID = EbrData[0] 
      orderby Location, RepName, AccountID). 
      Select((data, index) => new { 
       Record = new EbrRecord(
       AccountID = EbrData[0], 
       AccountName = EbrData[1], 
       MBSegment = EbrData[2], 
       RepName = EbrData[4], 
       Location = EbrData[7], 
       TsrLocation = EbrData[8] 
       ) 
       , 
       Index = index} 
       ).GroupBy(x => new {x.Record.Location, x.Record.RepName, batch = x.Index/100});  

"/ 100" 私に任意のバケットサイズを与える:batching with LINQにこの答えに触発

私のLINQ文は、次のようになります。 groupbyの他の要素は、バッチ間の均質性を達成するためのものです。私はこれがほとんど私が欲しいと思うが、それは私に次のコンパイラエラーを与える:A query body must end with a select clause or a group clause。私はなぜエラーを受け取っているのか理解していますが、全体的に私はこのクエリを修正する方法がわかりません。それはどうやって行なわれるのですか?

UPDATE私は非常に近く、以下に、私が後だ何を達成:

List<EbrRecord> input = new List<EbrRecord> { 
    new EbrRecord {Name = "Brent",Age = 20,ID = "A"}, 
    new EbrRecord {Name = "Amy",Age = 20,ID = "B"}, 
    new EbrRecord {Name = "Gabe",Age = 23,ID = "B"}, 
    new EbrRecord {Name = "Noah",Age = 27,ID = "B"}, 
    new EbrRecord {Name = "Alex",Age = 27,ID = "B"}, 
    new EbrRecord {Name = "Stormi",Age = 27,ID = "B"}, 
    new EbrRecord {Name = "Roger",Age = 27,ID = "B"}, 
    new EbrRecord {Name = "Jen",Age = 27,ID = "B"}, 
    new EbrRecord {Name = "Adrian",Age = 28,ID = "B"}, 
    new EbrRecord {Name = "Cory",Age = 29,ID = "C"}, 
    new EbrRecord {Name = "Bob",Age = 29,ID = "C"}, 
    new EbrRecord {Name = "George",Age = 29,ID = "C"}, 
    }; 

//look how tiny this query is, and it is very nearly the result I want!!! 
int i = 0; 
var result = from q in input 
       orderby q.Age, q.ID 
       group q by new { q.ID, batch = i++/3 }; 

foreach (var agroup in result) 
{ 
    Debug.WriteLine("ID:" + agroup.Key); 
    foreach (var record in agroup) 
    { 
     Debug.WriteLine(" Name:" + record.Name); 
    } 
} 

ここにトリックは閉鎖変数を使用することにより、選択し、「インデックス位置」overlaodをバイパスすることです(int iでこの場合)。出力結果は次のとおりです。

ID:{ ID = A, batch = 0 } 
Name:Brent 
ID:{ ID = B, batch = 0 } 
Name:Amy 
Name:Gabe 
ID:{ ID = B, batch = 1 } 
Name:Noah 
Name:Alex 
Name:Stormi 
ID:{ ID = B, batch = 2 } 
Name:Roger 
Name:Jen 
Name:Adrian 
ID:{ ID = C, batch = 3 } 
Name:Cory 
Name:Bob 
Name:George 

この回答は受け入れられますが、理想的な結果にはほんのわずかです。 2つではなく(Amy、Gabe、Noah)Amit、Gabeの3つのエントリーがあるはずです。これは、各グループが識別されたときにインデックスの位置がリセットされていないためです。誰もが各グループの私のカスタムインデックスの位置をリセットする方法を知っていますか?

更新2 私は答えを見つけた可能性があります。

int i = 0; 
string priorID = null; 
var result = from q in input 
       orderby q.Age, q.ID 
      group q by new { q.ID, batch = (BatchGroup(q.ID, ref priorID) ? i=0 : ++i)/3 }; 

今では私が欲しいものを行います。このようなLINQクエリを更新し、

public static bool BatchGroup(string ID, ref string priorID) 
    { 
     if (priorID != ID) 
     { 
      priorID = ID; 
      return true; 
     } 
     return false; 
    } 

第二に:まず、このような追加機能を作ります。私はちょうど私がその別個の機能を必要としないことを望む!

答えて

1
orderby Location, RepName, AccountID 

StriplingWarriorの答えに示されているように、上記した後、select句が必要です。 Linqの理解クエリはselectまたはgroup byで終わらなければなりません。


残念ながら、論理的欠陥がある...元のコードは、大きさ50の3つのバッチを生成する100のバッチサイズでIは第2グループの第1グループの50個のアカウントと100個のアカウントを持っていると仮定50,100の2バッチではありません。

これを修正する方法が1つあります。

IEnumerable<IGrouping<int, EbrRecord>> query = ... 

    orderby Location, RepName, AccountID 
    select new EbrRecord(
    AccountID = EbrData[0], 
    AccountName = EbrData[1], 
    MBSegment = EbrData[2], 
    RepName = EbrData[4], 
    Location = EbrData[7], 
    TsrLocation = EbrData[8]) into x 
    group x by new {Location = x.Location, RepName = x.RepName} into g 
    from g2 in g.Select((data, index) => new Record = data, Index = index }) 
       .GroupBy(y => y.Index/100, y => y.Record) 
    select g2; 


List<List<EbrRecord>> result = query.Select(g => g.ToList()).ToList(); 

またバッチにGROUPBYを使用すると、冗長な反復処理に非常に遅いことに注意してください。あなたは、順序付けされたセットの上で1パスでそれを行うforループを書くことができ、そのループはLinqToObjectsよりもはるかに高速に実行されます。

+0

intellisenseとコンパイラは、ドット表記に切り替わらない限り、 "select new"の後ろに "group by"を置くことを拒否します。 –

+0

"into x"は重要です。 –

+0

多くの厄介なタイプミスを修正しました。今私は終わった(それがうまくいくかどうか)。 –

2

これは機能しますか?

var query = (from line in EbrRecords 
     let EbrData = line.Split('\t') 
     let Location = EbrData[7] 
     let RepName = EbrData[4] 
     let AccountID = EbrData[0] 
     orderby Location, RepName, AccountID 
     select new EbrRecord(
       AccountID = EbrData[0], 
       AccountName = EbrData[1], 
       MBSegment = EbrData[2], 
       RepName = EbrData[4], 
       Location = EbrData[7], 
       TsrLocation = EbrData[8]) 
     ).Select((data, index) => new 
     { 
      Record = data, 
      Index = index 
     }) 
     .GroupBy(x => new {x.Record.Location, x.Record.RepName, batch = x.Index/100}, 
      x => x.Record); 
+0

私が期待するのは、EbrRecordリスト(リストのリスト)のリストです。しかし、上記はLocation、RepName、およびbatchだけを含む無名型のリストを私に与えます。私は実際にリンクした投稿が私が思っていた、または望んでいたことをしているのか疑問に思っています。 –

+0

@Brent:GroupByは、それぞれがLocation、RepName、およびバッチを持つ 'Key'を持ち、選択された値を含む' IEnumerable'自体でもある 'IGrouping'sの' IEnumerable'を作成します。あなたが私の更新された答えで過負荷を使うならば、効果的に 'IEnumerable >'を持つべきです。しかし、私はそれがあなたが望んだものを全くしない可能性が高いと思います。 David Bの答えを必ず読んでください。彼はいくつかの優れた点を作っている。 – StriplingWarrior