vba Excel 宏数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11456881/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-11 16:51:28  来源:igfitidea点击:

Excel Macro Arrays

arraysexcelvbaexcel-vba

提问by user1521458

Currently I have a macro that runs through a list and deletes duplicate values (in one column), but it's proving to be very inefficient. For every entry that it checks for duplicates, it has to run through the whole column; my file currently has 50,000 entries and that is no small task.

目前我有一个宏,它运行一个列表并删除重复值(在一列中),但它被证明是非常低效的。对于检查重复项的每个条目,它必须遍历整个列;我的文件目前有 50,000 个条目,这是一项不小的任务。

I think an easier way for the macro to work is for the macro to check if this value is in an array. If it is, then remove the row that the entry is in. If it isn't, add the value to the array.

我认为宏工作的一种更简单的方法是让宏检查这个值是否在数组中。如果是,则删除该条目所在的行。如果不是,则将该值添加到数组中。

Can someone provide some help with the basic outline of the macro? Thanks

有人可以提供有关宏基本轮廓的帮助吗?谢谢

回答by danielpiestrak

The Below code will loop through your source data and store it in an array, while simultaneously checking for duplicates. After the collection is complete it uses the array as a key to know which columns to delete.

下面的代码将遍历您的源数据并将其存储在一个数组中,同时检查重复项。收集完成后,它使用数组作为键来知道要删除哪些列。

Due to the high number of potentiol screen updates with the deletion be sure to turn screenupdating off. (included)

由于大量的potentiol 屏幕更新与删除一定要关闭screenupdating。(包括)

Sub Example()
    Application.ScreenUpdating = false
    Dim i As Long
    Dim k As Long
    Dim StorageArray() As String
    Dim iLastRow As Long
    iLastRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, "A").End(xlUp).Row

    ReDim StorageArray(1 To iLastRow, 0 To 1)

    'loop through column from row 1 to the last row
    For i = 1 To iLastRow
        'add each sheet value to the first column of the array
        StorageArray(i, 0) = ActiveSheet.Range("A" & i).Value
        '- keep the second column as 0 by default
        StorageArray(i, 1) = 0
        '- as each item is added, loop through previously added items to see if its a duplicate
        For k = 1 To i-1
            If StorageArray(k, 0) = StorageArray(i, 0) Then
                'if it is a duplicate set the second column of the srray to 1
                StorageArray(i, 1) = 1
                Exit For
            End If
        Next k
    Next i

    'loop through sheet backwords and delete rows that were maked for deletion
    For i = iLastRow To 1 Step -1
        If StorageArray(i, 1) = 1 Then
            ActiveSheet.Range("A" & i).EntireRow.Delete
        End If
    Next i

    Application.ScreenUpdating = true
End Sub


As requested, here is a similar way to do it, using Collections instead of an Array for key indexing: (RBarryYoung)

根据要求,这是一种类似的方法,使用集合而不是数组进行键索引:(RBarryYoung)

Public Sub RemovecolumnDuplicates()
    Dim prev as Boolean
    prev = Application.ScreenUpdating
    Application.ScreenUpdating = false
    Dim i As Long, k As Long

    Dim v as Variant, sv as String
    Dim cl as Range, ws As Worksheet
    Set ws = ActiveWorksheet    'NOTE: This really should be a parameter ...

    Dim StorageArray As New Collection
    Dim iLastRow As Long
    iLastRow = ws.Cells(ActiveSheet.Rows.Count, "A").End(xlUp).Row

    'loop through column from row 1 to the last row
    i = 1
    For k = 1 To iLastRow
        'add each sheet value to the collection
        Set cl = ws.Cells(i, 1)
        v = cl.Value
        sv = Cstr(v)

        On Error Resume Next
            StorageArray.Add v, sv
        If Err.Number <> 0 Then
            'must be a duplicate, remove it
            cl.EntireRow.Delete
            'Note: our index doesn't change here, since all of the rows moved
        Else
            'not a duplicate, so go to the next row
            i = i + 1
        End If
    Next k

    Application.ScreenUpdating = prev
End Sub

Note that this method does not need to assume any datatype or integer limits for the values of the cells in the column.

请注意,此方法不需要为列中的单元格的值假定任何数据类型或整数限制。

(Mea Culpa: I had to hand-enter this in Notepad, because my Excel is busy running project tests right now. So there may be some spelling/syntax errors...)

(Mea Culpa:我不得不在记事本中手动输入,因为我的 Excel 现在正忙于运行项目测试。所以可能存在一些拼写/语法错误......)

回答by Siddharth Rout

This is a followup to my comment. Looping 50k records+ Looping the Arraywill be an over kill for such a simple operation.

这是我的评论的后续。循环 50k 记录+循环数组对于这样一个简单的操作来说将是一种过度杀戮。

Like I mentioned in my comment, copy the values from the array to a new sheet. Then insert a blank column next to the 50k entries and do a Vlookupor CountIf. Once done, do an Autofilter and then delete the duplicate entries in 1 go. Let's take an example and see how this will work.

就像我在评论中提到的那样,将数组中的值复制到新工作表中。然后在 50k 条目旁边插入一个空白列并执行 aVlookupCountIf。完成后,执行自动过滤,然后一次性删除重复条目。让我们举一个例子,看看这将如何工作。

Let's say we have have an array with 1000 items? and in 1 sheet we have 50k data. The below code will be tested with 1000 items in Arrayand 50k DataSee Snapshot

假设我们有一个包含 1000 个项目的数组?在 1 张纸中,我们有 50k 数据。下面的代码将测试1000 items in Array50k Data查看快照

enter image description here

在此处输入图片说明

Paste this code in a module (The code took less then 5 secs to finish)

将此代码粘贴到模块中(该代码用了不到 5 秒的时间完成

enter image description here

在此处输入图片说明

Sub Sample()
    Dim ws As Worksheet, wstemp As Worksheet
    Dim LRow As Long
    Dim Ar(1 To 1000) As Long
    Dim startTime As String, EndTime As String

    startTime = Format(Now, "hh:mm:ss")

    Set ws = Sheets("Sheet1")
    Set wstemp = Sheets.Add

    '~~> Creating a dummy array
    For i = 1 To 1000
        Ar(i) = i
    Next i

    '~~> Copy it to the new sheet
    wstemp.Range("A1:A1000").Value = Application.Transpose(Ar)

    With ws
        LRow = .Range("A" & .Rows.Count).End(xlUp).Row

        .Columns(2).Insert Shift:=xlToRight
        .Range("B1").Value = "For Deletion"
        .Range("B2:B" & LRow).FormulaR1C1 = "=COUNTIF(" & wstemp.Name & "!C[-1],RC[-1])"
        .Columns(2).Value = .Columns(2).Value

        '~~> Remove any filters
        .AutoFilterMode = False

        '~~> Filter, offset(to exclude headers) and delete visible rows
        With .Range("B1:B" & LRow)
            .AutoFilter Field:=1, Criteria1:="<>0"
            .Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
        End With

        '~~> Remove any filters
        .AutoFilterMode = False

        .Columns(2).Delete
    End With

    EndTime = Format(Now, "hh:mm:ss")

    MsgBox "The process started at " & startTime & " and finished at" & EndTime
End Sub

回答by Excel Developers

For Excel 2007 and later: Copy the array to a sheet and use the removeduplicates method:

对于 Excel 2007 及更高版本:将数组复制到工作表并使用 removeuplicates 方法:

set ws = worksheets.add
ws.[A1].resize(ubound(yourarray,1),ubound(yourarray,2)).value = yourarray
ws.usedrange.removeduplicates columns:=1, header:=no

This assumes the lower bound of your array is 1, that the column you want to de-duplicate is column 1 and that your list has no headers. You can then find the borders of the new range and read it back into your array (erase the current array first).

这假设数组的下限为 1,您要重复数据删除的列是第 1 列,并且您的列表没有标题。然后您可以找到新范围的边界并将其读回您的数组(首先擦除当前数组)。

回答by Nick

I would suggest filltering your column and then use a formula to find the duplicates and delete them. I don't have the actually code for you (you didn't give us any code)

我建议填充您的列,然后使用公式查找重复项并删除它们。我没有你的实际代码(你没有给我们任何代码)

dim a as range
dim b as range
set a = Range ("A1")

Do while Not isEmpty(A)
Set b = a.offset(1,0)

If b = a then
b= ""
else a.offset (1,0)

Loop

I am sure you could put the filter in the code or just rember to fillter before you run the macro.

我相信您可以将过滤器放在代码中,或者在运行宏之前重新填充。