目前我有一个宏,它贯穿列表并删除重复值(在一列中),但它证明效率很低。对于每个检查重复项的条目,它必须遍历整个列;我的文件目前有50,000个条目,这不是一个小任务。Excel宏阵列
我认为宏工作的一个更简单的方法是让宏检查这个值是否在数组中。如果是,则删除条目所在的行。如果不是,则将该值添加到数组中。
有人可以提供一些关于宏的基本轮廓的帮助吗?谢谢
目前我有一个宏,它贯穿列表并删除重复值(在一列中),但它证明效率很低。对于每个检查重复项的条目,它必须遍历整个列;我的文件目前有50,000个条目,这不是一个小任务。Excel宏阵列
我认为宏工作的一个更简单的方法是让宏检查这个值是否在数组中。如果是,则删除条目所在的行。如果不是,则将该值添加到数组中。
有人可以提供一些关于宏的基本轮廓的帮助吗?谢谢
下面的代码将遍历源数据并将其存储在一个数组中,同时检查重复项。收集完成后,它使用该数组作为关键字来知道要删除哪些列。
由于删除的电位器屏幕更新次数很多,因此请务必关闭屏幕更新。 (含)
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
按照要求,在这里做一个类似的方式,使用集合而不是为关键索引数组:(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
注意,此方法不不需要为该列中的单元格的值假定任何数据类型或整数限制。
(过失:我不得不在记事本中手工输入这一点,因为我的Excel是忙碌的运行项目的测试,现在所以可能有一些拼写/语法错误...。)
这非常接近正确/最佳答案。您不应该假设范围也是活动工作表,您应该保存然后恢复当前的屏幕更新设置,最重要的是,您应该使用哈希或索引(集合)来检查是否存在,而不是 - 扫描整个StorageArray。如果你愿意,我可以为你做出这些改变。 – RBarryYoung 2012-07-12 18:15:40
另外,我刚刚注意到,您正在删除整行,但OP指定只修改一列。 – RBarryYoung 2012-07-12 18:22:40
感谢您的帮助;我认为这是最接近于工作的方式,但不是通过表格中的每个值来运行,我怎样才能让它只运行一列? – user1521458 2012-07-12 18:30:31
我会建议填充你的列,然后使用公式来找到重复项并删除它们。我没有为你的实际代码(你没有给我们任何代码)
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
我相信你可以把过滤器中的代码,或只是你的rember运行宏之前fillter。
这似乎只是检查列表中的先前值的范围值,而列表中的任何两个条目之间可能会出现重复。另外声明“else a.offset(1,0)”对我来说看起来像一个bug。 – RBarryYoung 2012-07-12 18:28:04
我试图保持简单,并假设重复将按顺序进行过滤。我现在很确定我的IF声明不会正确起作用。 – Nick 2012-07-12 20:18:24
这是一个后续对我的评论。 循环50k记录 + 循环阵列将是一个这样一个简单的操作过度杀死。
就像我在我的评论中提到的,将数组中的值复制到新工作表中。然后在50k条目旁边插入空白列并执行Vlookup
或CountIf
。完成后,执行一次自动筛选,然后删除重复条目。我们来举个例子,看看它是如何工作的。
假设我们有一个包含1000个项目的数组?在1页中我们有50k数据。下面的代码将与1000 items in Array
和50k Data
见快照
此代码的模块在粘贴进行测试(的代码了不到5秒以完成)
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
这似乎既缓慢又过于复杂。 – RBarryYoung 2012-07-12 18:16:37
5秒慢?你觉得哪个部分很复杂? :) – 2012-07-12 18:18:43
是的抱歉,我现在正在尝试它。谢谢你的帮助! – user1521458 2012-07-12 18:36:55
对于Excel 2007及更高版本:将数组复制到工作表并使用removeduplicates方法:
set ws = worksheets.add
ws.[A1].resize(ubound(yourarray,1),ubound(yourarray,2)).value = yourarray
ws.usedrange.removeduplicates columns:=1, header:=no
这里假定数组的下限为1,表示要删除的列是列1,并且列表中没有标题。然后可以找到新范围的边界并将其读回到阵列中(首先擦除当前阵列)。
将数组中的值复制到新工作表。然后在50k条目旁边插入一个空白列并做一个查找。完成后,执行一次自动筛选,然后删除重复条目。 – 2012-07-12 17:11:03
还有一个选择:循环访问数组,并在50k上做一个自动过滤器,并简单地将其逐个删除。一个比上述更慢的过程... – 2012-07-12 17:13:53
“删除”是指删除单元格内容,留下一个空白单元格,还是意味着删除该值并将所有其他值移动到一个单元格上?这对答案的复杂性和速度都有很大的影响。 (擦除更简单/更快)。 – RBarryYoung 2012-07-12 18:18:38