0

我们需要将以下逻辑形式的数据存储到一个类中:

"description 1", "activity A", "service A", Month 1 cost, month 2 cost, month 3 cost etc....

所以我有一个类对象,如下所示:

Public Class EntityTableRow
    Public Description As String
    Public Activity As String
    Public M1 as Double
    Public M2 as Double
    .....
End Class

M... 属性将根据源数据(excel 数据源)中的月份数来持有每月成本。从逻辑上讲,上述类将保存类似于上述逻辑形式的数据

现在我需要根据相同的列对行进行分组,并汇总每月的成本。

为此,我正在尝试使用以下 Linq 查询:

Dim a As New List(Of EntityTableRow) 
        a = myTable1.TableRows 
        Dim lFinal2 = From el In a Group el By Key = New With {Key el.Description, Key el.Activity} Into Group _ 
                      Select New With {.Activity = Key.Description, _ 
                                       .Country = Key.Activity, _ 
                                       .M1 = Group.Sum(Function(x) x.M1), _ 
                                       .M2 = Group.Sum(Function(x) x.M2)} 

这似乎工作正常,现在我该如何更改上面的 Linq 查询,对于下面的修改类,我需要将月份成本存储在字典中,并且仍然得到分组的行,并在不同的月份列上求和?

Public Class EntityTableRow
        Public Description As String
        Public Activity As String
        Public MonthCosts As New Dictionary(Of Integer, Double)
    End Class
4

2 回答 2

1
Dim lFinal2 = From el In a
              Group el By Key = New With {Key el.Description, Key el.Activity} Into Group
              Select New With {
                  .Activity = Key.Description,
                  .Country = Key.Activity,
                  .MonthCost =
                      (From k In Group.SelectMany(Function(g) g.MonthCosts.Keys).Distinct()
                       Select New With {
                            .Month = k,
                            .Sum = Group.Sum(Function(g) If(g.MonthCosts.ContainsKey(k), g.MonthCosts(k), 0))
                       }).ToDictionary(Function(i) i.Month, Function(i) i.Sum)
              }

简单的测试数据:

Dim a As New List(Of EntityTableRow) From {
    New EntityTableRow With {.Activity = "A", .Description = "D", .MonthCosts = New Dictionary(Of Integer, Double) From {{1, 20}, {2, 20}, {3, 20}}},
    New EntityTableRow With {.Activity = "A", .Description = "D", .MonthCosts = New Dictionary(Of Integer, Double) From {{2, 20}, {3, 20}, {4, 20}}}
}

结果: 在此处输入图像描述

于 2013-03-19T17:32:54.470 回答
-3

在我看来,您的初始数据类型应该采用以下形式

Public Class EntityTableRow
    Public Description As String
    Public Activity As String
    Public MonthCosts As IEnumerable(Of Decimal)
End Class

我将向您展示如何从订单中的一个IEnumerable(Of Decimal)和组中进行选择。这应该说明 IEnumerable 可以表示有序数据,具体取决于实现者。首先,我将扩展EntityTableRow类定义。使用不可变类有很大的好处,您应该同意直接公开没有属性的成员变量被认为是不好的做法。

Public Class EntityTableRow

    Private ReadOnly _descritption As String
    Private ReadOnly _activity As String
    Private ReadOnly _monthCosts As IList(Of Decimal)

    Public Sub New( _
            ByVal description As String,
            ByVal activity As String, _
            ByVal monthCosts As IEnumerable(Of Decimal))
        Me._description = description
        Me._activity = activity
        Me._monthCosts = New List(Of Decimal)(monthCosts)
    End Sub

    Public ReadOnly Property Description() As String
        Get
            Return Me._description
        End Get
    End Property

    Public ReadOnly Property Activity() As String
        Get
            Return Me._activity
        End Get
    End Property

    Public ReadOnly Property MonthCosts() As IEnumerable(Of Decimal)
        Get
            Return Me._monthCosts
        End Get
    End Property
End Class

您可以看到该类仅公开IEnumerable(Of Decimal),但此序列确实表示有序数据。现在我将展示如何使用 linq 对IEnumerable(Of EntityTableRow)使用此定义的数据进行分组。

首先,我将创建一些测试数据。

Dim testData As IEnumerable(Of EntityTableRow) = _
    {
        New EntityTableRow("A", "B", New Decimal() {20, 20, 20}),
        New EntityTableRow("A", "B", New Decimal() {Nothing, 20, 20, 20}),
        New EntityTableRow("C", "D", New Decimal() {10, 20, Nothing, 40}),
        New EntityTableRow("C", "D", New Decimal() {50, 60})
    }

在这一点上,我认为这IList将为稀疏的数据群体提供更好的支持,但这在原始问题中没有定义。这些示例单调地从一个开始。

有几种方法可以执行分组,我将把它分成三个简单的步骤,我希望很容易理解。请记住,这都是懒惰的评估。

首先,将数据展平到月份级别,将月份编号合并到序列中。

 Dim allCosts = testData.SelectMany( _
     Function(r) r.MonthCosts.Select( _
         Function(c, i) New With {r.Descriptionr, r.Activity, .Month = i, .Cost = c}))

请注意使用索引Select扩展从订单中推断月份。

接下来,按活动、描述和月份对成本进行分组和汇总,

Dim groupedCosts = allCosts.GroupBy( _
    Function(r) New With {r.Activity, r.Description, r.Month},
    Function(k, s) New With
        {
            k.Description,
            k.Activity,
            k.Month,
            .TotalCost = s.Sum(Function(r) r.Cost)
        })

这实际上为您提供了所需的信息,如果仍然需要,您可以通过描述和活动重新组合月份,

Dim groupedDescriptionActivities = groupedCosts.GroupBy( _
    Function(r) New With {r.Description, r.Activity}, _
    Function(k, s) New With
        {
            k.Description,
            k.Activity,
            .MonthCosts = s.Select(Function(r) New With {r.Month, r.TotalCost})
        })
于 2013-03-19T17:30:25.157 回答