我正在使用以下代码从excel文件中读取并将某些列添加到listview中。导入后我将其导出为CSV(代码未显示)。
我的问题是excel文件是一个摘要文件,它按事务显示数据,导致数千行。我想基于EPoS线执行相当于SUMIF
的excel,并在可能的情况下合并信息?
以下数据样本......
Public Structure ExcelRows
Dim Unit As String
Dim Outlet As String
Dim EPoS As String
Dim Quantity As String
Dim Value As String
Dim DateSale As String
End Structure
Public ExcelRowList As List(Of ExcelRows) = New List(Of ExcelRows)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Public Function GetInfo() As Boolean
Dim Completed As Boolean = False
Dim MyExcel As New Excel.Application
Dim enUK As New CultureInfo("en-GB")
Dim DOS As String = "01/04/15"
MyExcel.Workbooks.Open("C:\Dropbox\Tills\taRunAction1.xlsx")
MyExcel.Sheets("Report").Activate()
MyExcel.Range("A10").Activate()
Dim ThisRow As New ExcelRows
Do
If MyExcel.ActiveCell.Value > Nothing Or MyExcel.ActiveCell.Text > Nothing Then
ThisRow.Unit = MyExcel.ActiveCell.Value
MyExcel.ActiveCell.Offset(0, 1).Activate()
ThisRow.Outlet = MyExcel.ActiveCell.Value
MyExcel.ActiveCell.Offset(0, 1).Activate()
ThisRow.DateSale = MyExcel.ActiveCell.Value
MyExcel.ActiveCell.Offset(0, 2).Activate()
ThisRow.EPoS = MyExcel.ActiveCell.Value
MyExcel.ActiveCell.Offset(0, 1).Activate()
ThisRow.Quantity = MyExcel.ActiveCell.Value
MyExcel.ActiveCell.Offset(0, 1).Activate()
ThisRow.Value = MyExcel.ActiveCell.Value
ExcelRowList.Add(ThisRow)
MyExcel.ActiveCell.Offset(1, -6).Activate()
Else
Completed = True
Exit Do
End If
Loop
MyExcel.Workbooks.Close()
MyExcel = Nothing
Return Completed
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If GetInfo() = True Then
For Each xItem In ExcelRowList
Dim lViewItem As ListViewItem
lViewItem = ListView1.Items.Add(xItem.Unit)
lViewItem.SubItems.AddRange(New String() {xItem.Outlet, xItem.EPoS, xItem.Quantity, xItem.Value, xItem.DateSale})
Next
End If
End Sub
答案 0 :(得分:0)
执行此操作的一种方法是使用DataTable:
Private dt As DataTable
...
dt = New DataTable
dt.Columns.Add("EPOS", GetType(Integer))
dt.Columns.Add("Unit", GetType(String))
dt.Columns.Add("Quantity", GetType(Integer))
dt.Columns.Add("Value", GetType(Decimal))
dt.Columns.Add("Total", GetType(Decimal))
dt.Columns.Add("Date", GetType(DateTime))
Dim keys As DataColumn() = {dt.Columns("EPOS")}
dt.PrimaryKey = keys
目前尚不清楚" VALUE"是单价或销售金额,因为所有单位都是1.我不确定我会一遍又一遍地重复SaleDate等数据,关键是DataTable可以替换 XLRows结构。 / p>
一个关键是您希望使用正确的类型数据,以便您可以相乘和添加。 XLRow结构不正确输入 - 一切都是字符串而不是日期,十进制,整数等。
为此,将XLRow结构转换为类。它的作用是从XL获取字符串数据并转换为Typed数据。接下来,DataTable用于收集汇总数据。
为此,上面的PrimaryKey
定义至关重要。它会阻止你添加第二个(或第1000个)百事可乐"项目,并允许您找到该摘要项目。也许最重要的是,你可以摆脱ListView
并使用DataGridView
:
dgv.DataSource = dt
使用一行代码行,DGV将创建列并显示DataTable中的所有行。与ListView不同,它会在DataTable中的基础数据发生更改时自行更新。迭代数据以在循环内汇总:
' get the row for this EPOS code
Dim dr As DataRow = dt.Rows.Find(xl.EPOS)
If dr IsNot Nothing Then
' we already have this item, increment Quan, TotalSales:
dr("Quantity") += xl.Quantity
dr("Total") += (xl.Quantity * xl.Value)
Else
' new transaction item, add it:
dt.Rows.Add(xl.EPOS, xl.Unit, xl.Quantity,
xl.Value, (xl.Quantity * xl.Value), xl.DateSale)
End If
这非常简洁,因为当您从XL读取数据时,它会添加到摘要中。在执行汇总之前,无需将所有详细数据(XLS行)导入集合或数据表中。
为此省略DataTable中的主键。在这种情况下,DataTable将收集原始XLS数据而不是XLRow结构。 转换为数字以进行汇总。接下来,使用linq来总结数据;也许进入另一个DataTable。例如:
示例数据:
dt.Rows.Add(10001, "Ginger Ale", 1, 2.25, #4/5/2015#)
dt.Rows.Add(34582, "Pepsi", 3, 6.0, #4/5/2015#)
dt.Rows.Add(10002, "Chips", 1, 3.25, #4/5/2015#)
dt.Rows.Add(34582, "Pepsi", 1, 2.0, #4/5/2015#)
dt.Rows.Add(78301, "Roast Duck", 1, 15.25, #4/5/2015#)
dt.Rows.Add(34582, "Pepsi", 1, 2.0, #4/5/2015#)
dt.Rows.Add(34582, "Pepsi", 1, 2.0, #4/5/2015#)
dt.Rows.Add(10002, "Chips", 1, 3.25, #4/5/2015#)
dt.Rows.Add(34582, "Pepsi", 1, 2.0, #4/5/2015#)
获取摘要信息:
' group the data by EPOS code
Dim drs = From row In dt.AsEnumerable()
Group row By ID = row.Field(Of Integer)("EPOS") Into Group
Select Group
Dim TotSales As Decimal
Dim TotUnits As Integer
' each DRS is a collection of all the items with the same EPOS code
Dim dr As DataRow()
Console.WriteLine("EPOS Item Lines Units Total Sales")
' get the total sales in each group
For n As Integer = 0 To drs.Count - 1
dr = drs(n) ' the current EPOC group
TotUnits = dr.Sum(Function(t) t.Field(Of Integer)("Quantity"))
' Sales could just be TotUnits * dr(0)("Value")
' sample data makes it unclear if Value is the UNITPRICE or SALEAMOUNT
' This assumes it is SALEAMOUNT such that 2 Pepsi = 4.00
TotSales = dr.Sum(Function(t) t.Field(Of Decimal)("Value"))
' ToDo: do something interesting with the totals
Console.WriteLine("{0} {1} {2} {3} {4}",
dr(0)("EPOS"),
dr(0)("Unit").ToString,
dr.Length.ToString("D2"),
TotUnits.ToString,
TotSales.ToString("C2"))
Next
输出:
EPOS Item Lines Units Total Sales
10001 Ginger Ale 01 1 $2.25
34582 Pepsi 05 7 $14.00
10002 Chips 02 2 $6.50
78301 Roast Duck 01 1 $15.25
关键是,一旦在有用的结构中输入了类型数据,它的行为就像SUMIF一样。结果显示该数据有5个百事可乐条目,7个总单位和7 * 2 = 14。
我认为循环版本更易于管理和调试,而且由于在读取XLS行时随时构建了摘要,所以它更加经济。
答案 1 :(得分:0)
@ Plutonix的回答是一个好主意,我完全同意他关于将ExcelRows结构/类字段从字符串更改为适当的数字类型的意见。
另一种可能性是使用LINQ对ExcelRowList中的数据进行分组。类似于以下内容
Dim results = From r In ExcelRowList
Group By r.EPoS
Into Group, Sum(r.Value * r.Quantity)
请注意,这不会起作用,因为r.Value和r.Quantity是字符串,因此您无法将它们相乘。因此,您应该更改结构中的字段类型,然后在循环遍历单元格时,将单元格值转换为正确的类型。如果演员表失败,您将不得不决定该怎么做。