Return Index of an Element in an Array Excel VBA Return Index of an Element in an Array Excel VBA arrays arrays

Return Index of an Element in an Array Excel VBA


Dim pos, arr, valarr=Array(1,2,4,5)val = 4pos=Application.Match(val, arr, False)if not iserror(pos) then   Msgbox val & " is at position " & poselse   Msgbox val & " not found!"end if

Updated to show using Match (with .Index) to find a value in a dimension of a two-dimensional array:

Dim arr(1 To 10, 1 To 2)Dim xFor x = 1 To 10    arr(x, 1) = x    arr(x, 2) = 11 - xNext xDebug.Print Application.Match(3, Application.Index(arr, 0, 1), 0)Debug.Print Application.Match(3, Application.Index(arr, 0, 2), 0)

EDIT: it's worth illustrating here what @ARich pointed out in the comments - that using Index() to slice an array has horrible performance if you're doing it in a loop.

In testing (code below) the Index() approach is almost 2000-fold slower than using a nested loop.

Sub PerfTest()    Const VAL_TO_FIND As String = "R1800:C8"    Dim a(1 To 2000, 1 To 10)    Dim r As Long, c As Long, t    For r = 1 To 2000        For c = 1 To 10            a(r, c) = "R" & r & ":C" & c        Next c    Next r    t = Timer    Debug.Print FindLoop(a, VAL_TO_FIND), Timer - t    ' >> 0.00781 sec     t = Timer    Debug.Print FindIndex(a, VAL_TO_FIND), Timer - t    ' >> 14.18 secEnd SubFunction FindLoop(arr, val) As Boolean    Dim r As Long, c As Long    For r = 1 To UBound(arr, 1)    For c = 1 To UBound(arr, 2)        If arr(r, c) = val Then            FindLoop = True            Exit Function        End If    Next c    Next rEnd FunctionFunction FindIndex(arr, val)    Dim r As Long    For r = 1 To UBound(arr, 1)        If Not IsError(Application.Match(val, Application.Index(arr, r, 0), 0)) Then            FindIndex = True            Exit Function        End If    Next rEnd Function


array of variants:

    Public Function GetIndex(ByRef iaList() As Variant, ByVal value As Variant) As Long    Dim i As Long     For i = LBound(iaList) To UBound(iaList)      If value = iaList(i) Then       GetIndex = i       Exit For      End If     Next i    End Function

a fastest version for integers (as pref tested below)

    Public Function GetIndex(ByRef iaList() As Integer, ByVal value As Integer) As Integer     Dim i As Integer     For i = LBound(iaList) To UBound(iaList)      If iaList(i) = value Then: GetIndex = i: Exit For:     Next i    End Function' a snippet, replace myList and myValue to your varible names: (also have not tested)

a snippet, lets test the assumption the passing by reference as argument means something. (the answer is no) to use it replace myList and myValue to your variable names:

  Dim found As Integer, foundi As Integer ' put only once  found = -1  For foundi = LBound(myList) To UBound(myList):   If myList(foundi) = myValue Then    found = foundi: Exit For   End If  Next  result = found

to prove the point I have made some benchmarks

here are the results:

---------------------------Milliseconds---------------------------result0: 5 ' just empty loopresult1: 2702  ' function variant arrayresult2: 1498  ' function integer arrayresult3: 2511 ' snippet variant arrayresult4: 1508 ' snippet integer arrayresult5: 58493 ' excel function Application.Match on variant arrayresult6: 136128 ' excel function Application.Match on integer array---------------------------OK   ---------------------------

a module:

Public Declare Function GetTickCount Lib "kernel32.dll" () As Long#If VBA7 Then    Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr) 'For 64 Bit Systems#Else    Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) 'For 32 Bit Systems#End If    Public Function GetIndex1(ByRef iaList() As Variant, ByVal value As Variant) As Long    Dim i As Long     For i = LBound(iaList) To UBound(iaList)      If value = iaList(i) Then       GetIndex = i       Exit For      End If     Next i    End Function'maybe a faster variant for integers    Public Function GetIndex2(ByRef iaList() As Integer, ByVal value As Integer) As Integer     Dim i As Integer     For i = LBound(iaList) To UBound(iaList)      If iaList(i) = value Then: GetIndex = i: Exit For:     Next i    End Function' a snippet, replace myList and myValue to your varible names: (also have not tested)    Public Sub test1()     Dim i As Integer     For i = LBound(iaList) To UBound(iaList)      If iaList(i) = value Then: GetIndex = i: Exit For:     Next i    End SubSub testTimer()Dim myList(500) As Variant, myValue As VariantDim myList2(500) As Integer, myValue2 As IntegerDim nFor n = 1 To 500myList(n) = nNextFor n = 1 To 500myList2(n) = nNextmyValue = 100myValue2 = 100Dim oPMSet oPM = New PerformanceMonitorDim result0 As LongDim result1 As LongDim result2 As LongDim result3 As LongDim result4 As LongDim result5 As LongDim result6 As LongDim t As LongDim a As Longa = 0Dim i't = GetTickCountoPM.StartCounterFor i = 1 To 1000000Nextresult0 = oPM.TimeElapsed() '  GetTickCount - ta = 0't = GetTickCountoPM.StartCounterFor i = 1 To 1000000a = GetIndex1(myList, myValue)Nextresult1 = oPM.TimeElapsed()'result1 = GetTickCount - ta = 0't = GetTickCountoPM.StartCounterFor i = 1 To 1000000a = GetIndex2(myList2, myValue2)Nextresult2 = oPM.TimeElapsed()'result2 = GetTickCount - ta = 0't = GetTickCountoPM.StartCounterDim found As Integer, foundi As Integer ' put only onceFor i = 1 To 1000000found = -1For foundi = LBound(myList) To UBound(myList): If myList(foundi) = myValue Then  found = foundi: Exit For End IfNexta = foundNextresult3 = oPM.TimeElapsed()'result3 = GetTickCount - ta = 0't = GetTickCountoPM.StartCounterFor i = 1 To 1000000found = -1For foundi = LBound(myList2) To UBound(myList2): If myList2(foundi) = myValue2 Then  found = foundi: Exit For End IfNexta = foundNextresult4 = oPM.TimeElapsed()'result4 = GetTickCount - ta = 0't = GetTickCountoPM.StartCounterFor i = 1 To 1000000a = pos = Application.Match(myValue, myList, False)Nextresult5 = oPM.TimeElapsed()'result5 = GetTickCount - ta = 0't = GetTickCountoPM.StartCounterFor i = 1 To 1000000a = pos = Application.Match(myValue2, myList2, False)Nextresult6 = oPM.TimeElapsed()'result6 = GetTickCount - tMsgBox "result0: " & result0 & vbCrLf & "result1: " & result1 & vbCrLf & "result2: " & result2 & vbCrLf & "result3: " & result3 & vbCrLf & "result4: " & result4 & vbCrLf & "result5: " & result5 & vbCrLf & "result6: " & result6, , "Milliseconds"End Sub

a class named PerformanceMonitor

Option ExplicitPrivate Type LARGE_INTEGER    lowpart As Long    highpart As LongEnd TypePrivate Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As LARGE_INTEGER) As LongPrivate Declare Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As LARGE_INTEGER) As LongPrivate m_CounterStart As LARGE_INTEGERPrivate m_CounterEnd As LARGE_INTEGERPrivate m_crFrequency As DoublePrivate Const TWO_32 = 4294967296# ' = 256# * 256# * 256# * 256#Private Function LI2Double(LI As LARGE_INTEGER) As DoubleDim Low As Double    Low = LI.lowpart    If Low < 0 Then        Low = Low + TWO_32    End If    LI2Double = LI.highpart * TWO_32 + LowEnd FunctionPrivate Sub Class_Initialize()Dim PerfFrequency As LARGE_INTEGER    QueryPerformanceFrequency PerfFrequency    m_crFrequency = LI2Double(PerfFrequency)End SubPublic Sub StartCounter()    QueryPerformanceCounter m_CounterStartEnd SubProperty Get TimeElapsed() As DoubleDim crStart As DoubleDim crStop As Double    QueryPerformanceCounter m_CounterEnd    crStart = LI2Double(m_CounterStart)    crStop = LI2Double(m_CounterEnd)    TimeElapsed = 1000# * (crStop - crStart) / m_crFrequencyEnd Property


Here's another way:

Option Explicit' Just a little test stub. Sub Tester()    Dim pList(500) As Integer    Dim i As Integer    For i = 0 To UBound(pList)        pList(i) = 500 - i    Next i    MsgBox "Value 18 is at array position " & FindInArray(pList, 18) & "."    MsgBox "Value 217 is at array position " & FindInArray(pList, 217) & "."    MsgBox "Value 1001 is at array position " & FindInArray(pList, 1001) & "."End SubFunction FindInArray(pList() As Integer, value As Integer)    Dim i As Integer    Dim FoundValueLocation As Integer    FoundValueLocation = -1    For i = 0 To UBound(pList)        If pList(i) = value Then            FoundValueLocation = i            Exit For        End If    Next i    FindInArray = FoundValueLocationEnd Function