Получить дубликаты для двух столбцов с помощью LINQ - PullRequest
4 голосов
/ 23 сентября 2011

LINQ сводит меня с ума. Почему следующий запрос не возвращает дубликаты, а работает только с одним идентификатором? Где моя ошибка?

' 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}

Редактировать : Ниже приводится полное решение благодаря @ ответу Джона Скита :)

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 свойства. Следующая проблема в моем коде выше - получить дубликаты строк из исходной таблицы. Поскольку почти каждая строка имеет дубликат (согласно двум полям), результирующий DataTable содержал 99 из 100 строк, а не только 19 дублированных значений. Мне нужно было выбрать только первую дублирующую строку и соединить их с исходной таблицей на ПК.

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 , чтобы другие столбцы остались в том же контексте и вернуть только первый ряд сгруппированных дубликатов. На этом пути я могу использовать CopyToDataTable для создания DataTable из повторяющихся строк.

Всего несколько строк кода (я могу сохранить второй запрос, чтобы найти строки в исходной таблице), чтобы найти дубликаты в нескольких столбцах и создать из них DataTable.

1 Ответ

6 голосов
/ 23 сентября 2011

Проблема в том, как анонимные типы работают в VB - они изменяемы по умолчанию;только Key свойства включены для хеширования и равенства.Попробуйте это:

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

(В C # это не будет проблемой - анонимные типы в C # всегда неизменны во всех свойствах.)

...