Linq для получения общих значений при группировке DataTable - PullRequest
1 голос
/ 18 июня 2020

У меня есть DataTable, который содержит несколько столбцов, я хотел бы сгруппировать это DT по 1-му столбцу и проверить, есть ли общее значение в других 2 столбцах. Пример

enter image description here

Итак, я хотел бы сгруппировать эту таблицу по столбцу 1 (да / нет) и проверить, есть ли в этих группах кто-то из столбца 2, который в обеих группах, если да, то добавьте его в список. Это должно быть сделано в LinQ и VB. Net

Ответы [ 2 ]

1 голос
/ 19 июня 2020

Код здесь находится в C# и VB. Net.

Используя LINQ, вы можете легко группировать по Col1, а затем сравнивать группы. Я использовал Aggregate для обработки, когда Col1 имеет больше значений, чем просто yes и no.

var ans = dt.AsEnumerable()
            .GroupBy(r => r.Field<string>("Col1"), r => r.Field<string>("Col2"))
            .Aggregate(default(IEnumerable<string>), (ans, dg) => (ans == null) ? dg : ans.Intersect(dg));

В VB. Net это будет

Dim res = dt.AsEnumerable _
            .GroupBy(Function(r) r.Field(Of String)("Col1"), Function(r) r.Field(Of String)("Col2")) _
            .Aggregate(CType(Nothing, IEnumerable(Of String)), Function(ans, dg) If(ans Is Nothing, dg, ans.Intersect(dg)))

Существуют варианты, которые могут вернуть все DataRow или все совпадающие DataRow s, но они требуют создания специальных IEqualityComparer s или использования гораздо более сложных выражений LINQ с Where / Any / Concat для объедините их.

Например, это возвращает оба совпадения DataRow s для каждого Col2 совпадения:

var ans =   dt.AsEnumerable()
              .GroupBy(dr => dr.Field<string>("Col1"))
              .Aggregate(default(IEnumerable<DataRow>),
                         (ans, drg) => (ans == null) ? drg
                                                     : ans.Where(ls => drg.Any(dr => dr.Field<string>("Col2") == ls.Field<string>("Col2")))
                                                          .Concat(drg.Where(dr => ans.Any(ls => ls.Field<string>("Col2") == dr.Field<string>("Col2"))))
                        );

ПРИМЕЧАНИЕ: Intersect довольно эффективен по времени, хотя и использует дополнительные пространство для создания простого Set из второй коллекции. Второй пример не особенно эффективен по времени или пространству, и его лучше было бы сделать с пользовательскими IEqualityComparer и Intersect.

0 голосов
/ 19 июня 2020

Возможно, вы сможете убедить кого бы то ни было не использовать Linq. From https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1?view=netcore-3.1#hashset -and-linq-set-operations

LINQ обеспечивает доступ к операциям набора Distinct, Union, Intersect и Except для любого источника данных, который реализует IEnumerable или IQueryable интерфейсы. HashSet предоставляет более крупный и надежный набор операций над наборами. Например, HashSet предоставляет такие сравнения, как IsSubsetOf и IsSupersetOf.

Я создал 2 HashSet и добавил данные. Затем я использовал метод IntersectWith, чтобы изменить hsYes, чтобы он содержал только элементы, которые присутствуют в обеих коллекциях.

Private dt As New DataTable

Private Sub FillDataTable()
    dt.Columns.Add("Col1", GetType(String))
    dt.Columns.Add("Col2", GetType(String))
    dt.Columns.Add("Col3", GetType(String))
    dt.Rows.Add({"yes", "Frank", "LastName1"})
    dt.Rows.Add({"no", "Jessie", "LastName2"})
    dt.Rows.Add({"yes", "Sandra", "LastName3"})
    dt.Rows.Add({"yes", "Jessie", "LastName4"})
    dt.Rows.Add({"yes", "Frank", "LastName5"})
    dt.Rows.Add({"no", "Mike", "LastName6"})
    dt.Rows.Add({"no", "Stefan", "LastName7"})
End Sub

Private Sub GetMatches()
    Dim hsYes As New HashSet(Of String)
    Dim hsNo As New HashSet(Of String)
    For Each dr As DataRow In dt.Rows
        If (dr("Col1").ToString = "yes"Then
            hsYes.Add(dr("Col2").ToString)
        Else
            hsNo.Add(dr("Col2").ToString)) Then
        End If
    Next
    hsYes.IntersectWith(hsNo)
    For Each s In hsYes
        Debug.Print(s) 'Result Jessie
    Next
End Sub

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    FillDataTable()
End Sub

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    GetMatches()
End Sub
...