【问题标题】:Copying values from one column that matches a ID to a new sheet by creating new columns for each values through EXCEL VBA通过 EXCEL VBA 为每个值创建新列,将与 ID 匹配的列中的值复制到新工作表
【发布时间】:2012-08-07 16:29:51
【问题描述】:

我有一个要求,其中一个 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 来执行此操作。请帮助。

【问题讨论】:

  • 请使用代码标签 {} 格式化示例数据。到目前为止你有什么 - 你在哪里卡住了?

标签: excel vba grouping copying


【解决方案1】:

听起来像是数据透视表的工作。将 Order No 放在 Row 部分,将 Item 放在 Column 部分,将 Bill 放在 Values 部分。

【讨论】:

    【解决方案2】:

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

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

    Option Explicit
    
    Public ID As String
    Public ItemName As String
    

    将以下内容添加到类模块项

    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)

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

    希望对你有帮助

    【讨论】:

    • 效果很好 :):) 非常感谢....很棒的东西。但是是否可以考虑一个项目的所有实例,即,如果一个订单号中有两个“咖啡”或“比萨”,我需要在相邻列中更新它的两个值。如果需要,您可以为每个项目指定“4”列。这样我就可以获得所有数据和实例。那可能吗?我只需要将所有“项目”放在单独的列中。我们可以为每个项目分配一定的列数,以便项目适合其中的一个列。
    • 是的,我可以这样做,您能否将其作为另一个问题提出,以便我可以将代码发布到答案中,因为 cmets 只允许有限数量的 chrs?
    • 很抱歉回复晚了。当然我明天会做的。希望你能回复我。提前非常感谢。非常感谢你。
    • 您好我已经发布了这个问题,您可以使用我的用户 ID 搜索。请帮助我。非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多