Как сгруппировать по 2 значения в универсальном списке VB.NET?

Я использую VB.NET на платформе 2.0. Я хочу быстро сгруппировать общий список ‹> по двум свойствам. Ради этого примера допустим, что у меня есть список типа заказа со свойствами CustomerId, ProductId и ProductCount. Как мне получить среднее значение ProductCounts, сгруппированных по CustomerId и ProductId в VB.NET?

К сожалению, я не могу сделать это на уровне БД (что было бы легко). Также я не могу использовать LINQ или Lambada в качестве Im на Framewwork 2.0. Поэтому мне нужно сделать это на уровне приложения в VB.net. Список возвращается мне отсортированным по CustomerId и ProductId, поэтому я думаю, что с помощью нескольких циклов s я смогу создать среднее значение для, но должен быть более чистый способ сделать это?


person Mcad001    schedule 18.08.2010    source источник


Ответы (1)


Этот тип проблемы - отличный пример того, почему LINQ был введен в C # и VB.NET. В .NET 3.5 и выше LINQ предоставляет «более чистый способ», который вы ищете для решения этой проблемы.

К сожалению, поскольку вы используете .NET 2.0, вам придется решать проблему более или менее «вручную». Однако вы по-прежнему можете писать чистый код, инкапсулируя функциональность, которую вы ищете, в четко определенные классы и методы. Это одно (но не единственное) из преимуществ LINQ, то есть то, что он инкапсулирует ожидаемую вами функциональность в ясной, декларативной манере.

Вот пример кода для начала:

'The AggregateItem and AggregateItems classes will help encapsulate the '
'the functionality you are looking for.'

Public Class AggregateItem
    Public Property GroupByProperty1 As Integer ' Get/Set code...'
    Public Property GroupByProperty2 As Integer ' Get/Set code...'
    Public Property Values As List(Of Double) = New List(Of Double()() _
        ' Get/Set code...'

    Public Function GetAverage() As Double
        'Code to calculate and return Average...'
    End Function     
End Class

Public Class AggregateItems
    Public Property AggregateItemList As List(Of AggregateItem) = _
        New List(Of AggregateItem)() ' Get/Set code...'

    Public Sub InsertAggregateItem(groupByProperty1 As Integer, _
                                   groupByProperty2 As Integer, _
                                   value As Double)
        Dim aiExisting As AggregateItem
        aiExisting = GetMatchingAggregateItem(groupByProperty1, _
                                              groupByProperty2)
        If Not aiExisting Is Nothing Then
            aiExisting.Values.Add(value)
        Else
            aiExisting = New AggregateItem
            aiExisting.GroupByProperty1 = groupByProperty1
            aiExisting.GroupByProperty2 = groupByProperty2
            aiExisting.Values.Add(value)
            AggregateItemList.Add(aiExisting)
    End Sub

    Private Function GetMatchingAggregateItem(groupByProperty1 As Integer, _
                                              groupByProperty2 As Integer) _
            As AggregateItem
         Dim aiMatch As AggregateItem = Nothing
         For Each ag As AggregateItem in AggregateItemList
             If ag.GroupByProperty1 = groupByProperty1 AndAlso _
                ag.GroupByProperty2 = groupByProperty2 Then
                 aiMatch = ag
                 Exit For
             End If
         Next

         Return aiMatch
    End Function
Enc Class

'Then, to consume these classes....'

Public Module MyProgram
    Public Sub Main()
         Dim aItems As New AggregateItems()
         'Say you have List(Of Order) named listOfOrders'
         'We will loop through that list, insert the grouping IDs and values'
         'into our AggregateItems object'
         For Each o As Order In listOfOrders
            aItems.InsertAggregateItem(o.OrderId, o.ProductId, o.ProductCount)
         Next

        'Now we can loop through aItems to cleanly get the average: '
        For Each ai As AggregateItem in aItems.AggregateItemsList
            Console.WriteLine("Order: {0} Product: {1} Average: {2}", _
                ai.GroupByProperty1, ai.GroupByProperty2, _
                ai.GetAverage())
        Next
    End Sub

End Module

При вставке данных в хорошо инкапсулированные классы хорошо то, что потребляющий код очень лаконичен и прост для понимания. Кроме того, поскольку ваши данные уже агрегированы в класс AggregateItem, вы можете легко расширить этот класс с помощью дополнительных методов, таких как GetSum() или GetMax().

Очевидно, вы могли бы продолжить этот путь абстракции, чтобы лучше повторно использовать свой код, но я думаю, что это дает вам хорошее начало.

person Ben McCormack    schedule 18.08.2010
comment
Спасибо Бен. В итоге мне не нужно было агрегировать данные, мы подойдем к этому по-другому, так как в любом случае позже мы собираемся обновить наш appz до 3.5 .. Ваше решение, кажется, что-то для других, чтобы попробовать, если они столкнутся с аналогичным проблема, которая у меня была! :) - person Mcad001; 19.08.2010
comment
@Mcad Приятно слышать, что вы обновляетесь. Я не думаю, что у вас есть шанс убедить свою команду пойти дальше и перейти на 4.0? - person Ben McCormack; 19.08.2010