我想在VB.NET或C#中编写Linq子句的代码,它返回所有记录,直到其中一列值之和达到某个值并按其他列分组。
现在我在MySQL条款中有这个:
SELECT
O.Id,
O.Supp,
O.TotalCol,
(SELECT
sum(TotalCol) FROM Table1
WHERE Id <= O.Id) 'bTotal'
FROM Table1 O
HAVING bTotal <= 50000
我已完成的代码:
对象类:
Class Invoices
Public id As Integer
Public supplier As String
Public total As Decimal
Public Sub New(ByVal id As Integer, ByVal supplier As String, ByVal total As Decimal)
Me.id = id
Me.supplier = supplier
Me.total = total
End Sub
End Class
未分组列表:
Dim LInvoicesPrincipal As New List(Of Invoices)()
Dim InvoiceItem As Invoices
InvoiceItem = New Invoices(1, "SUPP 1", 45000)
LInvoicesPrincipal.Add(InvoiceItem)
InvoiceItem = New Invoices(2, "SUPP 1", 6000)
LInvoicesPrincipal.Add(InvoiceItem)
InvoiceItem = New Invoices(3, "SUPP 2", 15000)
LInvoicesPrincipal.Add(InvoiceItem)
InvoiceItem = New Invoices(4, "SUPP 2", 6000)
LInvoicesPrincipal.Add(InvoiceItem)
InvoiceItem = New Invoices(5, "SUPP 1", 4000)
LInvoicesPrincipal.Add(InvoiceItem)
LINQ中的分组,由提供者(我需要将总数之和限制为某个值的规则(准确地说是50000)):
Dim LGroups
LGroups = _
From oInvoices In LInvoicesPrincipal _
Group oInvoices By oInvoices.supplier _
Into Group Select Group
允许我验证分组的For Each:
For Each EachList As Invoices() In LGroups
For Each EachItem As Invoices In EachList
Next
Next
所有这些目前都会返回我这样的列表:
Group 1:
Invoices(1, "SUPP 1", 45000)
Invoices(2, "SUPP 1", 6000)
Invoices(5, "SUPP 1", 4000)
Group 2:
Invoices(3, "SUPP 2", 15000)
Invoices(4, "SUPP 2", 6000)
但是......我想得的是这样的:
Group 1:
Invoices(1, "SUPP 1", 45000)
Invoices(5, "SUPP 1", 4000)
Group 2:
Invoices(2, "SUPP 1", 6000)
Group 3:
Invoices(3, "SUPP 2", 15000)
Invoices(4, "SUPP 2", 6000)
(值的总和必须<=每组50000次
答案 0 :(得分:1)
有Bin Packing algorithm用于限制打包给定项目集所需的箱数。您似乎最关心的是将供应商的总计限制为&lt; = 50000与创建多少个发票组。 按供应商元素似乎仅在传递时提及。
你已经有Invoice
课程,但我的课程略有不同:
Public Class Invoice
Public Property Id As Int32
Public Property Supplier As String
Public Property Total As Decimal
Public Property GroupId As Int32
...
我强烈建议您使用实际的Properties
代替公共字段。这允许您在DataGridView
中显示结果以进行调试。新的GroupId
属性允许代码识别哪些发票项目已添加到InvoiceGroup
:
Public Class InvoiceGrp
Public Property GroupId As Int32
Public Property Supplier As String
Public ReadOnly Property Total As Decimal
Get
Return Invoices.Sum(Function(k) k.Total)
End Get
End Property
Public Property Invoices As New List(Of Invoice)
...
还有一个ToString()
覆盖和一个Count
属性用于调试。首先,&#39; raw&#39;发票清单需要按供应商分组,然后才能处理每个发票清单:
' group the invoices by supplier
Dim grpData = Invoices.GroupBy(Function(g) g.Supplier).ToArray()
' collection to store the results
Dim groupedInvoices = New List(Of InvoiceGrp)
Dim groupId = 1
' create invoice groups one supplier group at a time
For Each item In grpData
Dim gi = GroupInvoices(item, 50000, groupId) ' add ToArray() for debug
groupedInvoices.AddRange(gi)
' the GroupId gets incremented for each supplier
' group, use the last one for the start value in the next
' supplier group
groupId = gi.Max(Of Int32)(Function(k) k.GroupId)
Next
发票总额是随机选择的:
{4000, 2500, 5000, 10000, 15000, 45000, 6000, 25000}
。
。 GroupInvoice
方法和助手:
Private Iterator Function GroupInvoices(grp As IEnumerable(Of Invoice),
Limit As Decimal,
grpId As Int32) As IEnumerable(Of InvoiceGrp)
' make a copy so we can remove those grouped
Dim myGrp = New List(Of Invoice)(grp.
Where(Function(k) k.GroupId = -1).
OrderByDescending(Function(k) k.Total).
ToArray())
While (myGrp.Count > 0)
grpId += 1
' NewInvoiceGroup does the actual Group creation
Dim newGrp = NextInvoiceGroup(myGrp, Limit, grpId)
' remove grouped items from the ToDo list
myGrp.RemoveAll(Function(r) newGrp.Invoices.Contains(r))
Yield newGrp
End While
End Function
Private Function NextInvoiceGroup(items As List(Of Invoice),
Limit As Decimal,
nextGrp As Int32) As InvoiceGrp
' this creates one InvGrp with as many Invoices
' as will fit.
Dim InvG = New InvoiceGrp With {.Supplier = items(0).Supplier, .GroupId = nextGrp}
For Each inv In items
If InvG.Total + inv.Total <= Limit Then
' tag Invoice with the InvoiceGrp Id
inv.GroupId = nextGrp
InvG.Invoices.Add(inv)
End If
Next
Return InvG
End Function
重要提示
N
的linq结果中的每个组都会传递到GroupInvoices
。NextInvoiceGroup
实际创建的总计不超过50k。Iterator
,允许它在创建时返回每个组。GroupId
,因此代码知道哪些已添加到组中。NextInvoiceGroup
会迭代所有发票,将每个发票添加到新的InvoiceGrp
,直到达到50k或用完项目。GroupId
应为所有群组的唯一值Iterator
方法让您感到困惑,可以重构以创建并返回List(Of InvoiceGroup)
调试转储:
For Each grp In groupedInvoices
Console.WriteLine("{0} Grp# {1}: ct: {2} tot: {3}", grp.Supplier, grp.GroupId,
grp.Invoices.Count, grp.Total)
For Each inv In grp.Invoices
Console.WriteLine("Supp: {0} Inv#: {1} Tot: {2}",
inv.Supplier, inv.Id, inv.Total)
Next
Console.WriteLine()
Next
结果(非随机供应商组除外)
Alpha Grp#1:ct:4 tot:50000
补充:Alpha Inv#:2 Tot:25000
补充:Alpha Inv#:14 Tot:15000
补充:Alpha Inv#:8 Tot:5000
补充:Alpha Inv#:16 Tot:5000Alpha Grp#2:ct:3 tot:13000
补充:Alpha Inv#:22 Tot:5000
补充:Alpha Inv#:1 Tot:4000
补充:Alpha Inv#:6 Tot:4000Delta Grp#3:ct:2 tot:50000
补充:Delta Inv#:4 Tot:45000
补充:Delta Inv#:3 Tot:5000Delta Grp#4:ct:5 tot:22500
补充:Delta Inv#:5 Tot:6000
补充:Delta Inv#:21 Tot:6000
补充:Delta Inv#:11 Tot:4000
补充:Delta Inv#:12 Tot:4000
补充:Delta Inv#:9 Tot:2500Echo Grp#5:ct:2 tot:50000
补充:Echo Inv#:23 Tot:45000
补充:Echo Inv#:24 Tot:5000Echo Grp#6:ct:2 tot:50000
补充:Echo Inv#:7 Tot:25000
Supp:Echo Inv#:18 Tot:25000Echo Grp#7:ct:3 tot:50000
补充:Echo Inv#:20 Tot:25000
补充:Echo Inv#:19 Tot:15000
Supp:Echo Inv#:25 Tot:10000FoxTrot Grp#8:ct:2 tot:50000
补充:FoxTrot Inv#:15 Tot:45000
Supp:FoxTrot Inv#:13 Tot:5000FoxTrot Grp#9:ct:1 tot:45000
补充:FoxTrot Inv#:17 Tot:45000FoxTrot Grp#10:ct:1 tot:6000
Supp:FoxTrot Inv#:10 Tot:6000