【问题标题】:Looping through an array while grabbing certain elements在抓取某些元素时循环遍历数组
【发布时间】:2017-08-31 18:40:25
【问题描述】:

我有一个看起来像这样的巨大数据集

我正在尝试列出不同公司的列表,并为每个公司选择 3 个并将它们合并。根据上面的照片,我将有 2 个不同的列表,每个列表有 3 家公司(TH Repair 除外,最终列表中有 2 家)。

我的真实数据集包含数百家不同的公司,每个公司都有数十/数百个条目,因此我将完成数十个列表(每个列表可能长达数百个)。

我尝试录制一个宏,结果得到了这段代码

Sub Loop1()
'
' Loop1 Macro
'

'
    Range("A4:E6").Select
    Selection.Copy
    Sheets("Sheet3").Select
    Range("A18").Select
    ActiveSheet.Paste
    Sheets("Sheet2").Select
    Range("A11:E13").Select
    Application.CutCopyMode = False
    Selection.Copy
    Sheets("Sheet3").Select
    Range("A21").Select
    ActiveSheet.Paste
    Sheets("Sheet2").Select
    Range("A17:E19").Select
    Application.CutCopyMode = False
    Selection.Copy
    Sheets("Sheet3").Select
    Range("A24").Select
    ActiveSheet.Paste
End Sub

然而,事实证明这比我预期的要复杂得多。

我正在寻找看起来像这样的最终结果

【问题讨论】:

  • 在我重新输入您的所有示例数据后,我很快就会有一个解决方案。
  • 非常感谢!
  • 迈克斯汽车店 7 号去哪儿了?它在您的第一张图片中,但不在第二张图片的列表 1 或 2 中
  • @PrincessPeach2091 不要屏住呼吸,我猜 Jeeped 是在讽刺你拍照而不是提供 CSV 或其他东西。
  • @don_freem 它会出现在列表 3 中

标签: arrays vba excel loops


【解决方案1】:

看看这样的东西是否适合你。我只通过它运行了一个场景,因此您需要对其进行更多测试。

  • 这假设数据按原始工作表上的 B 列排序
  • 此过程假设第 1 行有标题或没有数据。
  • 您需要将此行Set ws1 = ActiveWorkbook.Worksheets("Sheet1") 中的“Sheet1”更改为您开始使用的工作表的名称。

    Option Explicit
    
    Public Sub MoveData()
    
        Dim ws1 As Worksheet
        Set ws1 = ActiveWorkbook.Worksheets("Sheet1")
    
        Dim ws2 As Worksheet
        Set ws2 = ActiveWorkbook.Worksheets.Add()
    
    
        Dim rw As Long
        Dim match_count As Integer
        Dim list_multiplier As Integer
        list_multiplier = 7
        Dim list_row() As Long
        ReDim list_row(0)
        list_row(0) = 2
    
        For rw = 2 To ws1.Range("A" & ws1.Rows.Count).End(xlUp).Row
    
    
            If ws1.Range("B" & rw).Value <> ws1.Range("B" & rw).Offset(-1, 0).Value Then
                match_count = 0
            Else
                match_count = match_count + 1
            End If
    
            Dim list_num As Integer
            list_num = match_count \ 3
    
            If list_num > UBound(list_row, 1) Then
                ReDim Preserve list_row(list_num)
                list_row(list_num) = 2
            End If
    
            ws2.Cells(list_row(list_num), 1 + list_multiplier * list_num).Value = ws1.Range("A" & rw).Value
            ws2.Cells(list_row(list_num), 2 + list_multiplier * list_num).Value = ws1.Range("B" & rw).Value
            ws2.Cells(list_row(list_num), 3 + list_multiplier * list_num).Value = ws1.Range("C" & rw).Value
            ws2.Cells(list_row(list_num), 4 + list_multiplier * list_num).Value = ws1.Range("D" & rw).Value
            ws2.Cells(list_row(list_num), 5 + list_multiplier * list_num).Value = ws1.Range("E" & rw).Value
            list_row(list_num) = list_row(list_num) + 1
    
        Next rw
    
    End Sub
    

【讨论】:

  • 实际上我的真实数据集是数百家公司,所以我想要几十个不同的列表(尽管仍然从每家公司获取 3 个!)我将尝试这段代码。谢谢
  • @PrincessPeach2091 是所有新列表并排在一张纸上,彼此堆叠,还是不同的纸?
  • 因为会有多达 100 个不同的,我认为并排在一张纸上是最简单的。
  • 我编辑了这段代码。现在试试这个。它应该继续添加其他列表,直到用完列为止。
【解决方案2】:

录制宏时,请确保已启用开发人员功能区选项卡上的“使用相对引用”,:)

【讨论】:

    【解决方案3】:

    假设第 3 行有你的数据标题,你可以试试这个:

    Option Explicit
    
    Sub main()
        Dim nLists As Long, iList As Long
        Dim data As Variant
        Dim dataToDelete As Range
    
        With Range("F3", Cells(Rows.Count, 1).End(xlUp))
            data = .Value
            nLists = WorksheetFunction.Max(.Resize(,1))
            nLists = nLists \ 3 + IIf(nLists - 3 * (nLists \ 3) = 0, -1, 0)
        End With
    
        With Range("A3").Resize(, 6)
            For iList = 0 To nLists
                Set dataToDelete = Nothing
                With .Offset(, iList * 6).Resize(UBound(data))
                    .Value = data
                    .AutoFilter Field:=1, Criteria1:="<=" & iList * 3, Criteria2:=">" & (iList + 1) * 3, Operator:=xlOr
                    If Application.WorksheetFunction.Subtotal(103, .Cells) > 1 Then Set dataToDelete = .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible)
                    .Parent.AutoFilterMode = False
                    If Not dataToDelete Is Nothing Then dataToDelete.Delete xlShiftUp
                End With
            Next
        End With
    End Sub
    

    【讨论】:

    • 嗯,我不断收到“运行时 1004 错误。“无法获取 WorksheetFunction 类的 Max 属性”imgur.com/a/dVf4o
    • 您在链接中的数据与您的问题中的数据不同,其中 A 列对每个不同的公司都有累进编号:您不能要求帮助显示某种示例数据,然后惊叹于提供的解决方案不使用完全不同类型的数据!因此,请使用最终版本的数据来编辑您的问题。谢谢
    • 抱歉!这些数字仅供参考,但我将它们添加到我的真实数据集中并且它有些工作。它提取了一些奇怪的数据imgur.com/a/cOg8P
    • 它们为什么很奇怪?
    • 它抓取公司的随机版本,而不是我试图获得的每家公司的 3 个:/
    【解决方案4】:

    您的任务实际上比您的在线建议所建议的要复杂一些。基本上,您必须执行以下操作:

    1. 找出您拥有的唯一“键”(即 B 列中的唯一项)的数量。这将告诉您您需要的总行数(即唯一键数 * 3)
    2. 计算每个“键”的项目数。这将告诉您需要多少列(即最大项目数 / 3 * 数组 [A:E = 5] 中的列数)
    3. 循环遍历每一行数据,然后为该“键”放置适当的行。达到 3 后,向右跳到该关键 6 列的列,然后继续。

    如果您要使用 Class 对象和 Collection 类型的对象,这可能是非常简洁的代码,但从您的帖子来看,您正处于 VBA 编程之旅的开始。因此,我将每个任务分解为单独的代码块,以便您希望了解数组如何为您工作。稍微练习一下数组后,也许您可​​以尝试通过组合一些循环来提高这段代码的效率:

    Public Sub RunMe()
        Dim data As Variant
        Dim r As Long, c As Long, i As Long, dataRows As Long, dataCols As Long, keyLen As Long, maxCount As Long
        Dim keys As String
        Dim k As Variant
        Dim keyArray() As String
        Dim keyCount() As Long, threeCount() As Long, rowNum() As Long, colNum() As Long
        Dim output() As Variant
    
        'Read the data - change "Sheet1" to your sheet name.
        'Shows how to write range values into a variant to
        'create an array of variants.
        data = ThisWorkbook.Worksheets("Sheet1").UsedRange.Value2
        dataRows = UBound(data, 1)
        dataCols = UBound(data, 2)
    
        'Create a list of unique keys.
        'Note: not the most efficient way, but shows how to
        'create an array from a value-separated string.
        For r = 1 To dataRows
            If InStr(keys, CStr(data(r, 2))) = 0 Then
                If Len(keys) > 0 Then keys = keys & "|"
                keys = keys & CStr(data(r, 2))
            End If
        Next
        keyArray = Split(keys, "|")
        keyLen = UBound(keyArray)
    
        'Initialise the row and column numbers for each key.
        'Shows how to iterate an array using For Each loop.
        ReDim rowNum(keyLen)
        ReDim colNum(keyLen)
        r = 1
        i = 0
        For Each k In keyArray
            rowNum(i) = r
            colNum(i) = 1
            r = r + 3
            i = i + 1
        Next
    
        'Count the number of items for each key.
        'Shows how to iterate an array using For [index] loop.
        ReDim keyCount(keyLen)
        For r = 1 To dataRows
            i = IndexOfKey(keyArray, CStr(data(r, 2)))
            keyCount(i) = keyCount(i) + 1
            If keyCount(i) > maxCount Then maxCount = keyCount(i)
        Next
    
        'Size the output array.
        c = WorksheetFunction.Ceiling(maxCount / 3, 1)
        ReDim output(1 To (keyLen + 1) * 3, 1 To c * dataCols + c - 1)
    
        'Populate the output array.
        ReDim threeCount(keyLen)
        For r = 1 To dataRows
            i = IndexOfKey(keyArray, CStr(data(r, 2)))
            'Copy the columns for this row.
            For c = 1 To dataCols
                output(rowNum(i), colNum(i) + c - 1) = data(r, c)
            Next
            'Increment the count and if it's equals 3 then
            'reset the row num and increase the column number.
            threeCount(i) = threeCount(i) + 1
            rowNum(i) = rowNum(i) + 1
            If threeCount(i) = 3 Then
                rowNum(i) = rowNum(i) - 3
                colNum(i) = colNum(i) + dataCols + 1
                threeCount(i) = 0
            End If
        Next
    
        'Write the data - change "Sheet2" to your sheet name.
        'Shows how to write an array to a Range.
        ThisWorkbook.Worksheets("Sheet2").Range("A3") _
            .Resize(UBound(output, 1), UBound(output, 2)).Value = output
    End Sub
    
    Private Function IndexOfKey(list() As String, key As String) As Long
        Dim i As Long
        Dim k As Variant
    
        'Helper function to find index position of key in array.
        For Each k In list
            If key = k Then
                IndexOfKey = i
                Exit Function
            End If
            i = i + 1
        Next
    
        IndexOfKey = -1
    End Function
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多