自定义查找function
我正在尝试创build一个函数,在search整个活动工作表后,将返回包含特定string的单元格的总数。 很像在“查找和replace”中find的“x单元格”。
我到目前为止:
Function FINDIST(stringToFind) Dim counter As Integer: counter = 0 For Each Cell In ActiveSheet.UsedRange.Cells If InStr (Cell, stringToFind) > 0 Then counter = counter + 1 End If Next End Function
另一种做法是:
Function FINDIST(stringToFind) As Long FINDIST = Evaluate("SUM(IFERROR(SEARCH(" & Chr(34) _ & "*" & stringToFind & "*" & Chr(34) & "," _ & ActiveSheet.UsedRange.Address & ",1),0))") End Function
这将在所使用范围内的每个单元格中searchstringToFind
,如果在单元格中find该string,则返回一个数组1,如果未find该string,则返回错误。 IFERROR
部分将错误转换为零, SUM
将得到的二进制数组相加。
这只会在每个单元格中发生一次stringToFind
,即使它出现多次,但是看着你的代码,我认为这就是你正在寻找的东西。
我希望它有帮助!
UPDATE
出于好奇,我做了一些testing,看看这两种方法是如何比较的(直接读取范围与使用评估)。 这是我使用的代码:
Option Explicit Private Declare Function GetTickCount Lib "kernel32.dll" () As Long Sub test() Dim ticks As Long Range("A1:AA100000").Value = "adlrkjgalbabyajglakrjg" ticks = GetTickCount FINDIST1 ("baby") Debug.Print "Read from range: ", GetTickCount - ticks ticks = GetTickCount FINDIST ("baby") Debug.Print "Evaluate: ", GetTickCount - ticks End Sub Function FINDIST(stringToFind) As Long FINDIST = Evaluate("SUM(IFERROR(SEARCH(" & Chr(34) _ & "*" & stringToFind & "*" & Chr(34) & "," _ & ActiveSheet.UsedRange.Address & ",1),0))") End Function Function FINDIST1(stringToFind) As Long Dim counter As Long: counter = 0 Dim c As Range Dim firstAddress As String With ActiveSheet.UsedRange Set c = .Find(stringToFind, LookIn:=xlValues, LookAt:=xlPart) If Not c Is Nothing Then firstAddress = c.Address Do counter = counter + 1 Set c = .FindNext(c) Loop While Not c Is Nothing And c.Address <> firstAddress End If End With FINDIST1 = counter End Function
更新2
克里斯·尼尔森在下面的评论中提出了两个很好的观点:
-
ActiveSheet.Evaluate
比Application.Evaluate
更快。 查尔斯·威廉姆斯在评论中的文本链接解释了这种行为。 - 好的旧的
Variant
数组比其他任何方法都要好。
为了完整起见,我发布了我testing的variant
数组方法的版本:
Function FINDIST_looping(stringToFind) As Long Dim vContents, lRow As Long, lCol As Long, lCounter As Long vContents = ActiveSheet.UsedRange.Value2 For lRow = LBound(vContents, 1) To UBound(vContents, 1) For lCol = LBound(vContents, 2) To UBound(vContents, 2) lCounter = IIf(InStr(vContents(lRow, lCol), stringToFind), _ lCounter + 1, lCounter) Next lCol Next lRow FINDIST_looping = lCounter End Function
Doug Glancy提出了另一个非常好的观点,即COUNTIF
可以用来代替SEARCH
。 这导致一个非数组公式的解决scheme,应该支配我的原始公式,在性能方面。
这是道格的公式:
FINDIST_COUNTIF = ActiveSheet.Evaluate("COUNTIF(" _ & ActiveSheet.Cells.Address & "," & Chr(34) & "*" _ & stringToFind & "*" & Chr(34) & ")")
实际上,Doug的观点意味着不需要Evaluate()
。 我们可以从WorksheetFunction
对象调用Countif
。 因此,如果目标是从电子表格中调用此函数,则不需要使用Evaluate()
或将其包含在UDF
– 这是一个带有通配符的典型COUNTIF
应用程序。
结果:
Read from range: 247,495 ms (~ 4 mins 7 secs) Application.Evaluate: 3,261 ms (~ 3.2 secs) Variant Array: 1,706 ms (~ 1.7 secs) ActiveSheet.Evaluate: 1,257 ms (~ 1.3 secs) ActiveSheet.Evaluate (DG): 602 ms (~ 0.6 secs) WorksheetFunction.CountIf (DG):550 ms (~ 0.55 secs)
看起来, Application.Evaluate
比使用Range.Find()
( Range.Find()
快大约75倍。而且,原始代码( Integer
变为Long
)在大约8秒内运行。
另外,在这种情况下, Activesheet.Evaluate
似乎比Variant
数组更快。 调用CountIf
作为一个WorksheetFunction
方法与Evaluate
它之间的区别似乎很小。
CAVEAT :在UsedRange
findstringToFind
的频率可能会影响这几个方法的相对性能。 我跑了Activesheet.Evaluate
和Variant Array
方法与上述范围(A1:AA100000)
但只有十个第一个单元格具有匹配的string。
结果(平均6次运行,差异非常小):
Activesheet.Evaluate: 920 ms (~ 1. sec) Variant Array: 1654 ms (~ 1.7 secs)
这很有趣 – 看起来ActiveSheet.Evaluate
在这种情况下比变体数组有更好的性能(除非我在循环代码中做了一些可怕的事情,在这种情况下请让我知道)。 另外, Variant
方法的性能实际上是相对于string的频率不变的。
运行在Win7
下的EXCEL 2010
进行。
与Tony Dallimore使用Find的build议一起工作,并将返回types更改为Long。
MSDN文章: http : //msdn.microsoft.com/en-us/library/office/ff839746( v= office.15).aspx
Function FINDIST(stringToFind) As Long Dim counter As Long: counter = 0 Dim c As Range Dim firstAddress As String With ActiveSheet.UsedRange Set c = .Find(stringToFind, LookIn:=xlValues, , LookAt:=xlPart) If Not c Is Nothing Then firstAddress = c.Address Do counter = counter + 1 Set c = .FindNext(c) Loop While Not c Is Nothing And c.Address <> firstAddress End If End With FINDIST = counter End Function
查找通常比编码相当快,但我没有速度testing其他任何东西,如果它是快或慢,将有兴趣在这里。