2013-03-19 88 views
0

我們需要以下邏輯形式的數據存儲到一個類:Linq查詢到組,和字典對象

"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 
+0

如果您的成本價值是使用'雙'的財務是一個壞主意。 「小數」更適合。 – Jodrell 2013-03-19 17:26:49

+0

「MonthCosts」字典中的Keys列表是否在來自同一組的項目之間有所不同? – MarcinJuraszek 2013-03-19 17:26:54

回答

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

和結果: enter image description here

+0

你是一個Linq魔術師。謝謝!你的邏輯起作用! – 2013-03-20 01:54:02

+0

使用'IDctionary'或任何其他鍵控或索引的集合確實允許稀疏數據。雖然最初沒有在問題中指定,但這可能很有用。 +1的Linq。 – Jodrell 2013-03-20 12:13:52

-3

這聽起來我喜歡你的初始數據類型應採取的形式

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

我會告訴你如何從IEnumerable(Of Decimal)和組的順序選擇。這應該說明IEnumerable可以表示有序數據,取決於實現者。首先,我將擴展​​類定義。有重要的benefits when using immutable classes,你應該同意直接暴露你的成員變量沒有屬性被認爲是不好的做法。

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

在這一點上,我認爲accapet將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}) 
     }) 
+1

IEnumerable只是一個集合,您將失去發生特定成本的那個月的上下文。 – Rich 2013-03-19 17:33:33

+0

@這些數據將按順序列舉,否?你失去的是非規範化的模式。 – Jodrell 2013-03-19 17:34:37

+0

@Jodrell是的,但每個項目可以從不同的月份開始,幷包含不同數量的項目。 – MarcinJuraszek 2013-03-19 17:35:35