0

我有一个要求,其中一个 ID 下有一组值(此 ID 对于每个组都是唯一的)。我希望通过 excel VBA 为每个值创建新列,将组的值复制到新工作表中。说,这是我的主要工作表

订单号。账单项目
==============================
12345 100 比萨
12345 200 巧克力
12345 300 咖啡
12345 400 比萨1
12345 500 饮料
12456 600 比萨
12456 700 巧克力
12456 800 比萨1
12360 900 比萨
12360 1000 巧克力
12360 1100 咖啡

我想要像下面这样的o/p:

订单号。PIZZA PIZZA1 CHOCO COFFEE COFFEE1 饮料
==================================================== ==============  
12345 100 400 200 300 500  
12456 600 800 700    
12360 900 1000 1100

我希望将主工作表中存在的值复制到新工作簿的相应列中,例如“PIZZA”值应该根据正确的“订单号”复制到新工作簿。如在主表中。需要一个 excel VBA 来做到这一点。请帮助。

4

2 回答 2

1

听起来像是数据透视表的工作。将订单号放在“行”部分,将“项目”放在“列”部分,将“帐单”放在“值”部分。

于 2012-08-07T18:45:21.390 回答
0

使用名为 Sheet1 和 Sheet2 的工作表创建一个新工作簿(可以通过更改代码中的常量来更改名称)添加一个主模块和 3 个类模块(在 VBA 编辑器中单击 Inser > Module 和 Insert Class Module,Alt F11 开始)重命名类模块如下:Bill、Item 和 Order

将以下代码添加到 Class 模块 Bill

Option Explicit

Public ID As String
Public ItemName As String

将以下内容添加到 Class 模块 Item

Option Explicit

Public Name As String
Public ColumnNumber As Long

Private Sub Class_Initialize()
    ColumnNumber = 0
End Sub

将以下代码添加到类模块顺序

Option Explicit

Public Bills As Collection
Public ID As String

Public Sub AddBill(BillID As String, ItemName As String)
    Dim B As Bill

    Set B = New Bill
    B.ID = BillID
    B.ItemName = ItemName
    Bills.Add B
End Sub

Private Sub Class_Initialize()
    Set Bills = New Collection

End Sub

将以下代码添加到您的主穆勒

Option Explicit
Const ORDER_TXT As String = "Order No." 'text in the header cell for order number column
Const INPUT_SHEET_NAME As String = "Sheet1"
Const OUTPUT_SHEET_NAME As String = "Sheet2"
Const FIRST_OUTPUT_COL As Long = 2
Const FIRST_OUTPUT_ROW As Long = 2


Dim Orders As Collection
Dim Items As Collection

Sub process_data()

Dim sh As Worksheet
Dim HeaderRow As Long
Dim HeaderCol As Long
Dim CurRow As Long
Dim CurOrder As Order
Dim CurItemCol As Long
Dim CurItem As Item
Dim CurBill As Bill

'Get Info from input sheet
CurItemCol = FIRST_OUTPUT_COL + 1
HeaderRow = 1
HeaderCol = 1
Set Orders = New Collection
Set Items = New Collection

If FindCell(ORDER_TXT, INPUT_SHEET_NAME, sh, HeaderRow, HeaderCol, False) Then
CurRow = HeaderRow + 1
Do While sh.Cells(CurRow, HeaderCol).Value <> ""
    Set CurOrder = GetOrder(sh.Cells(CurRow, HeaderCol).Value)
    If sh.Cells(CurRow, HeaderCol + 1).Value <> "" Then
        If sh.Cells(CurRow, HeaderCol + 2).Value <> "" Then
            Set CurItem = GetItem(sh.Cells(CurRow, HeaderCol + 2).Value)
            If CurItem.ColumnNumber = 0 Then
                'its a new item
                CurItem.ColumnNumber = CurItemCol
                CurItemCol = CurItemCol + 1
            End If
            'now add this bill to the order
            Call CurOrder.AddBill(sh.Cells(CurRow, HeaderCol + 1).Value, CurItem.Name)
        End If 'could add else with error message here
    End If
    CurRow = CurRow + 1
Loop

'now put data on output sheet
'find output sheet
For Each sh In ThisWorkbook.Sheets
    If sh.Name = OUTPUT_SHEET_NAME Then Exit For
Next

'Add check here that we found the sheet

CurRow = FIRST_OUTPUT_ROW
'write headers
sh.Cells(CurRow, FIRST_OUTPUT_COL).Value = ORDER_TXT
For Each CurItem In Items
    sh.Cells(CurRow, CurItem.ColumnNumber).Value = CurItem.Name
Next
'Write Orders
For Each CurOrder In Orders
    CurRow = CurRow + 1
    sh.Cells(CurRow, FIRST_OUTPUT_COL).Value = CurOrder.ID
    For Each CurBill In CurOrder.Bills
        sh.Cells(CurRow, GetColumnNumber(CurBill.ItemName)).Value = CurBill.ID
    Next
Next

End If
End Sub
Function GetColumnNumber(ItemName As String) As Long
Dim I As Item

GetColumnNumber = 1 'default value
For Each I In Items
    If I.Name = ItemName Then
        GetColumnNumber = I.ColumnNumber
        Exit Function
    End If
Next
End Function
Function GetOrder(OrderID As String) As Order
Dim O As Order

For Each O In Orders
    If O.ID = OrderID Then
        Set GetOrder = O
        Exit Function
    End If
Next
'if we get here then we didn't find a matching order
Set O = New Order
Orders.Add O
O.ID = OrderID
Set GetOrder = O

End Function
Function GetItem(ItemName As String) As Item
Dim I As Item

For Each I In Items
    If I.Name = ItemName Then
        Set GetItem = I
        Exit Function
    End If
Next
'if we get here then we didn't find a matching Item
Set I = New Item
Items.Add I
I.Name = ItemName
Set GetItem = I

End Function
Function FindCell(CellText As String, SheetName As String, sh As Worksheet, row As Long, col As Long, SearchCaseSense As Boolean) As Boolean
Const GapLimit As Long = 10

'searches the named sheet column at a time, starting with the column and row specified in row and col
'gives up on each row if it finds GapLimit empty cells
'gives up on search if it finds do data un GapLimit columns

Dim RowFails As Long
Dim ColFails As Long
Dim firstrow As Long

FindCell = False
firstrow = row
ColFails = 0
RowFails = 0

'find sheet
For Each sh In ThisWorkbook.Sheets
    If sh.Name = SheetName Then Exit For
Next

If sh.Name = SheetName Then
    Do 'search columns
        ColFails = ColFails + 1
        Do  'search column
            If sh.Cells(row, col).Value = "" Then
                RowFails = RowFails + 1
            Else
                If ((sh.Cells(row, col).Value = CellText And SearchCaseSense) Or (UCase(sh.Cells(row, col).Value) = UCase(CellText) And (Not SearchCaseSense))) Then
                    FindCell = True
                    Exit Function
                End If
                RowFails = 0
                ColFails = 0
            End If
            row = row + 1
        Loop While RowFails <= GapLimit
        col = col + 1
        row = firstrow
        RowFails = 0
    Loop While ColFails < GapLimit
End If
End Function

运行例程 process_data(来自 excel 的 Alt F8)

该程序不考虑同一订单上具有相同商品(例如咖啡)的多张账单,只会出现一张账单,我不知道您想如何处理这种情况。代码需要检查和错误处理例程,以使其对无效数据具有鲁棒性,我添加了一些注释作为提示。

希望这可以帮助

于 2012-08-07T19:26:22.437 回答