2011-09-23 3 views
4

LINQは私を狂わせます。なぜ後続のクエリは重複を返さないのに対し、1つの識別子だけで動作しますか?私のエラーはどこですか?LINQで2列の重複を取得

' generate some test-data ' 
Dim source As New DataTable 
source.Columns.Add(New DataColumn("RowNumber", GetType(Int32))) 
source.Columns.Add(New DataColumn("Value1", GetType(Int32))) 
source.Columns.Add(New DataColumn("Value2", GetType(Int32))) 
source.Columns.Add(New DataColumn("Text", GetType(String))) 
Dim rnd As New Random() 
For i As Int32 = 1 To 100 
    Dim newRow = source.NewRow 
    Dim value = rnd.Next(1, 20) 
    newRow("RowNumber") = i 
    newRow("Value1") = value 
    newRow("Value2") = (value + 1) 
    newRow("Text") = String.Format("RowNumber{0}-Text", i) 
    source.Rows.Add(newRow) 
Next 
' following query does not work, it always has Count=0 ' 
' although it works with only one identifier ' 
Dim dupIdentifiers = From row In source 
     Group row By grp = New With {.Val1 = row("Value1"), .Val2 = row("Value2")} 
     Into Group 
     Where Group.Count > 1 
     Select idGroup = New With {grp.Val1, grp.Val2, Group.Count} 

編集:続いて、@Jon Skeet's answerのおかげで完全なソリューションです:)

Dim dupKeys = From row In source 
     Group row By grp = New With {Key .Val1 = CInt(row("Value1")), Key .Val2 = CInt(row("Value2"))} 
     Into Group Where Group.Count > 1 
     Select RowNumber = CInt(Group.FirstOrDefault.Item("RowNumber")) 

Dim dupRows = From row In source 
     Join dupKey In dupKeys 
     On row("RowNumber") Equals dupKey 
     Select row 

If dupRows.Any Then 
    ' create a new DataTable from the first duplicate rows ' 
    Dim dest = dupRows.CopyToDataTable 
End If 

グループの主な問題は、私はそれらkeyプロパティしなければならないということでした。 上記のコードの次の問題は、元のテーブルから重複した行を取得することでした。 ほとんどすべての行に(2つのフィールドに従って)重複があるため、結果のDataTableには99の100行が含まれ、19の重複値だけでなく、最初の重複行だけを選択し、それらをPK上の元のテーブルと結合する必要がありました。

Select RowNumber = CInt(Group.FirstOrDefault.Item("RowNumber")) 

これは私の場合には動作しますが、多分誰かが私は複合キーを持っていたならば、元のテーブルからのみ重複を選択する方法を私に説明することができます。


編集

Dim dups = From row In source 
     Group By grp = New With {Key .Value1 = CInt(row("Value1")), Key .Value2 = CInt(row("Value2"))} 
     Into Group Where Group.Count > 1 
     Let Text = Group.First.Item("Text") 
     Select Group.First 

If dups.Any Then 
     Dim dest = dups.CopyToDataTable 
End If 

私は他の列(複数可保つためにLet-Keywordが必要:I'vはので、ここで私が必要とするすべてで、質問の最後の部分を自分で答え)を同じコンテキストに変換し、グループ化されたdupの最初の行のみを返します。このように私はCopyToDataTableを使用して、重複する行からDataTableを作成することができます。

複数の列で重複を見つけてそれらのDataTableを作成するために、全体的にわずか2行のコード(元のテーブルの行を見つけるために2番目のクエリを保存できます)

答えて

6

問題は方法ですanonymous types work in VB - デフォルトでは変更可能です。ハッシュと平等のためには、Keyのプロパティのみが含まれています。これを試してみてください:

Group row By grp = New With {Key .Val1 = row("Value1"), Key .Val2 = row("Value2")} 

(C#では、これは問題ではないだろう - C#での匿名型は、すべてのプロパティでは常に不変です)

+0

ありがとうございました。私はあなたの質問を編集しました。なぜなら、選択が遅すぎたからです。私は最初に行をグループ化する必要があります。私はこの事実を覚えていればいいと思う。 –

+0

@TimSchmelter:おっと、申し訳ありません。しかし、それは今働きますか? –

+0

はい、重複は正しいです。しかし、今私は重複して元のテーブルに参加したい次の行に立ち往生しています。結果( 'dupRows')はdups(19行)だけでなく、フルテーブル(100行)です。 –

関連する問題