+ Reply to Thread
Results 1 to 23 of 23

VBA Code Using Arrays To Be Optimized

Hybrid View

  1. #1
    Forum Expert NeedForExcel's Avatar
    Join Date
    03-16-2013
    Location
    Pune, India
    MS-Off Ver
    Excel 2016:2019, MS 365
    Posts
    3,876

    VBA Code Using Arrays To Be Optimized

    Hi,

    On the attached file, when a Value from Cell D1 is selected, it outputs corresponding values from Column E separated by commas..

    All working as desired with this code -

    Sub Conc()
    
        Dim InputVal As String, R As Variant, Counter As Integer, NewVal(), C As Integer
    
        InputVal = Range("D1").Value2
    
        R = Range("A1").CurrentRegion
    
        For Counter = LBound(R,1) To UBound(R,1)
            If R(Counter, 1) = InputVal Then
                C = C + 1
                ReDim Preserve NewVal(1 To C)
                NewVal(C) = R(Counter, 2)
            End If
        Next Counter
    
        Range("E1").Value2 = InputVal & " " & Join(NewVal, ",")
    
    End Sub
    I would like to know If the code can be further optimized? For example better use of arrays, loops etc.
    Attached Files Attached Files
    Last edited by NeedForExcel; 07-22-2015 at 02:04 AM.
    Cheers!
    Deep Dave

  2. #2
    Forum Guru TMS's Avatar
    Join Date
    07-15-2010
    Location
    The Great City of Manchester, NW England ;-)
    MS-Off Ver
    MSO 2007,2010,365
    Posts
    44,975

    Re: VBA Code Using Arrays To Be Optimized

    Given that the objective of the exercise is to play with arrays, probably not. In practice, as the desire is to produce a comma separated string, you might as well build a string from the outset.

    Regards, TMS
    Trevor Shuttleworth - Retired Excel/VBA Consultant

    I dream of a better world where chickens can cross the road without having their motives questioned

    'Being unapologetic means never having to say you're sorry' John Cooper Clarke


  3. #3
    Forum Guru
    Join Date
    07-25-2011
    Location
    Florida
    MS-Off Ver
    Excel 2003
    Posts
    9,643

    Re: VBA Code Using Arrays To Be Optimized

    On small suggestion; this does one Redim Preserve instead of one for each match.

    Sub Conc()
        
        Dim InputVal As String, R As Variant, Counter As Integer, NewVal(), C As Integer, OP
        
        InputVal = Range("D1").Value2
        
        R = Range("A1").CurrentRegion
        
        ReDim NewVal(1 To UBound(R))
        
        For Counter = LBound(R) To UBound(R)
            If R(Counter, 1) = InputVal Then
                C = C + 1
                NewVal(C) = R(Counter, 2)
            End If
        Next Counter
        
        ReDim Preserve NewVal(1 To C)
        
        Range("E1").Value2 = InputVal & " " & Join(NewVal, ",")
        
    End Sub
    Surround your VBA code with CODE tags e.g.;
    [CODE]your VBA code here[/CODE]
    The # button in the forum editor will apply CODE tags around your selected text.

  4. #4
    Forum Guru Kyle123's Avatar
    Join Date
    03-10-2010
    Location
    Leeds
    MS-Off Ver
    365 Win 11
    Posts
    7,238

    Re: VBA Code Using Arrays To Be Optimized

    Use a dictionary, don't redim preserve. It's nasty since it copies the entire array each time you want to add to it

  5. #5
    Valued Forum Contributor
    Join Date
    09-17-2012
    Location
    Johannesburg, South Africa
    MS-Off Ver
    Excel 2007
    Posts
    454

    Re: VBA Code Using Arrays To Be Optimized

    Notwithstanding all the comments about redim preserve, here's some points and code you can try:

    ConcatCodes.xlsm

    Your example seems to imply that you only need to do a single code search at a time. For this, your code looks quite efficient as it is. I'd just make it friendlier for normal spreadsheet use by turning it into a user defined function.

    Public Function JoinCodes(SourceRng As Excel.Range, FindCode As String) As String
        Dim R As Variant, Counter As Integer, NewVal(), C As Integer
        Application.Volatile
        R = SourceRng.Value
        For Counter = LBound(R) To UBound(R)
            If R(Counter, 1) = FindCode Then
                C = C + 1
                ReDim Preserve NewVal(1 To C)
                NewVal(C) = R(Counter, 2)
            End If
        Next Counter
        JoinCodes = FindCode & " " & Join(NewVal, ",")
    End Function
    With this you don't need a 'firing' mechanism to run the macro, ie. your Worksheet_Change code. You can just enter this formula in E1:

    =JoinCodes($A$1:$B$16,$D1)
    But if you really are looking for speed optimisation, then the one logical improvement I'd apply is to do all the codes in one pass. In theory, this code should be a speedup, but your testing may prove me wrong. The procedure outputs a 2-column table of results using TargetCell as the top-left corner.

    Public Sub JoinCodeTable(SourceRng As Excel.Range, TargetCell As Excel.Range)
        Dim InputVal As String
        Dim R As Variant
        Dim Counter As Integer
        Dim NewVal As Variant
        Dim C As Integer
        Dim KeyCol As New Collection
        Dim Key As String
        Dim KeyArr As Variant
        Dim TransArr As Variant
        Dim i As Long
        Dim KeyError As Boolean
        InputVal = Range("D1").Value2
        R = SourceRng
        For Counter = LBound(R) To UBound(R)
            Key = R(Counter, 1)
            On Error Resume Next
            i = KeyCol(Key)
            KeyError = (Err <> 0)
            On Error GoTo 0
            If KeyError Then
                If Not IsArray(KeyArr) Then
                    i = 0
                    ReDim KeyArr(0 To 1, i)
                Else
                    i = UBound(KeyArr, 2) + 1
                    ReDim Preserve KeyArr(0 To 1, i)
                End If
                Dim KeyVals As Variant
                KeyArr(0, i) = Key
                KeyArr(1, i) = KeyVals
                KeyCol.Add i, Key
            End If
            NewVal = KeyArr(1, i)
            If Not IsArray(NewVal) Then
                C = 1
                ReDim NewVal(1 To C)
            Else
                C = UBound(NewVal) + 1
                ReDim Preserve NewVal(1 To C)
            End If
            NewVal(C) = R(Counter, 2)
            KeyArr(1, i) = NewVal
        Next Counter
        ReDim TransArr(UBound(KeyArr, 2), 1)
        For i = 0 To UBound(KeyArr, 2)
            TransArr(i, 0) = KeyArr(0, i)
            TransArr(i, 1) = Join(KeyArr(1, i), ",")
        Next
        TargetCell.Resize(UBound(TransArr, 1) + 1, UBound(TransArr, 2) + 1).Value2 = TransArr
    End Sub
    Called like this:
    Public Sub DoAll_Click()
        JoinCodeTable Range("A1").CurrentRegion, Range("D3")
    End Sub
    Illustration:
    ConcatCodes.jpg

  6. #6
    Forum Expert NeedForExcel's Avatar
    Join Date
    03-16-2013
    Location
    Pune, India
    MS-Off Ver
    Excel 2016:2019, MS 365
    Posts
    3,876

    Re: VBA Code Using Arrays To Be Optimized

    @cyiangou - Thank you for the input..

    Actually the thing is, I am learning how to use Arrays effectively along with Code Optimization..

    Hence, I'v posted this code, so that the members can guide me where I can further improve..

  7. #7
    Forum Expert NeedForExcel's Avatar
    Join Date
    03-16-2013
    Location
    Pune, India
    MS-Off Ver
    Excel 2016:2019, MS 365
    Posts
    3,876

    Re: VBA Code Using Arrays To Be Optimized

    @TMS - Thank you for the suggestion..

    @AlphaFrog - You are right with the Redim thing.. I should have thought of it myself..

    @Kyle - How can this be done using dictionaries? I am trying, but I am unable to get it working..
    Last edited by NeedForExcel; 07-22-2015 at 05:54 AM.

  8. #8
    Forum Expert NeedForExcel's Avatar
    Join Date
    03-16-2013
    Location
    Pune, India
    MS-Off Ver
    Excel 2016:2019, MS 365
    Posts
    3,876

    Re: VBA Code Using Arrays To Be Optimized

    Hey Kyle,

    I got it working using Scripting.Dictionary

    Sub UseDict()
    
        Dim Dict As Scripting.Dictionary, R As Variant, Counter As Integer, InputVal As String
        Set Dict = New Scripting.Dictionary
        
        InputVal = Range("D1").Value2
        
        With Dict
            R = Application.Transpose(Range("A1:B" & Cells(Rows.Count, 1).End(xlUp).Row).Value2)
            
            For Counter = LBound(R, 2) To UBound(R, 2) 
                If .Exists(R(1, Counter)) Then
                    .Item(R(1, Counter)) = .Item(R(1, Counter)) & "," & R(2, Counter)
                Else
                    .Add (R(1, Counter)), R(2, Counter)
                End If
            Next Counter
            Range("E2").Value2 = InputVal & " " & .Item(InputVal)
        End With
        
    End Sub
    Does this look fine? The only issues I am facing is the Array limit of 65k rows for Transpose
    Last edited by NeedForExcel; 07-22-2015 at 06:49 AM.

  9. #9
    Forum Guru Kyle123's Avatar
    Join Date
    03-10-2010
    Location
    Leeds
    MS-Off Ver
    365 Win 11
    Posts
    7,238

    Re: VBA Code Using Arrays To Be Optimized

    Having re-read your question, why not simply?:
        Dim InputVal As String
        Dim R As Variant
        Dim Counter As Long
        Dim temp As String
        
        InputVal = Range("D1").Value2
    
        R = Range("A1").CurrentRegion.Value2
    
        
        For Counter = LBound(R) To UBound(R)
            If R(Counter, 1) = InputVal Then temp = temp & R(Counter, 2) & ","
        Next Counter
    
        Range("E1").Value2 = InputVal & " " & Left$(temp, Len(temp) - 1)

  10. #10
    Valued Forum Contributor
    Join Date
    09-17-2012
    Location
    Johannesburg, South Africa
    MS-Off Ver
    Excel 2007
    Posts
    454

    Re: VBA Code Using Arrays To Be Optimized

    benchtests.xlsm

    I reconfigured your original code into a benchmarkable format, and ran it 300 000 times x 5 separate runs, and got these times in seconds:
    1.652344, 1.664063, 1.640625, 1.640625, 1.632813
    That's pretty fast, told you so.

    But we are optimizing, so I tested the following changes:
    • Changed NewVal to string array
    • Dimensioned it only once, to the maximum number of codes possible (ie. # of rows in source table)
    • Only do one Redim Preserve at the end (just to get it to the right length for the Join function).

    Again, I ran it 300 000 times x 5 separate runs, and got these times in seconds:
    1.375, 1.378906, 1.40625, 1.394531, 1.433594

    I also tested your last Dictionary based Sub, I ran it 300 000 times x 2 separate runs, and got these times in seconds:
    25.30469, 27.78906

  11. #11
    Forum Guru Kyle123's Avatar
    Join Date
    03-10-2010
    Location
    Leeds
    MS-Off Ver
    365 Win 11
    Posts
    7,238

    Re: VBA Code Using Arrays To Be Optimized

    Your tests aren't equal.

    On the arrays, you're populating R outside the loop, for the dictionary, you're doing it inside.

    Move it outside the loop and the times are much closer.

    Using your test, building the string was around 5x faster than using arrays on my machine:
        Dim R As Variant, Counter As Long, InputVal As String, temp As String
        Dim Tests As Variant, test As Variant, t As Variant, i As Long
        
        Tests = Array("XXX0001", "XXX0002", "XXX0003")
        InputVal = Range("D1").Value2
        R = Range("A1").CurrentRegion.Value2
           
        t = Timer
        For i = 1 To 100000
            For Each test In Tests
                For Counter = LBound(R, 2) To UBound(R, 2)
                    If R(Counter, 1) = InputVal Then temp = temp & R(Counter, 2) & ","
                Next Counter
                temp = ""
            Next test
        Next i
        Debug.Print Timer - t
    Last edited by Kyle123; 07-22-2015 at 07:57 AM.

  12. #12
    Forum Guru TMS's Avatar
    Join Date
    07-15-2010
    Location
    The Great City of Manchester, NW England ;-)
    MS-Off Ver
    MSO 2007,2010,365
    Posts
    44,975

    Re: VBA Code Using Arrays To Be Optimized

    @Kyle; that was my point in post #2.

    Regards, TMS

  13. #13
    Forum Guru Kyle123's Avatar
    Join Date
    03-10-2010
    Location
    Leeds
    MS-Off Ver
    365 Win 11
    Posts
    7,238

    Re: VBA Code Using Arrays To Be Optimized

    Sorry Trevor, missed that

  14. #14
    Valued Forum Contributor
    Join Date
    09-17-2012
    Location
    Johannesburg, South Africa
    MS-Off Ver
    Excel 2007
    Posts
    454

    Re: VBA Code Using Arrays To Be Optimized

    @Kyle123

    Shucks, I missed that in the Dictionary version; I added that one just before making my post. But that is the only one populating R in the loop.

    But for the first two, the difference in array initialization is deliberate. That's the optimization - the design of Conc2 does not require that the array/dictionary be initialized for every distinct calculation. The same array just gets reused.

    If I was optimizing one of my apps, that would be a significant consideration. One setup, many batches. If you were instantiating a class, you initialize the class instance, then use its methods. I usually don't care much whether the setup is optimized or not; it's usually a tiny fraction of total run time.

  15. #15
    Valued Forum Contributor
    Join Date
    09-17-2012
    Location
    Johannesburg, South Africa
    MS-Off Ver
    Excel 2007
    Posts
    454

    Re: VBA Code Using Arrays To Be Optimized

    Basic rule of optimisation: Remove unnecessary operations from your inner loops.

  16. #16
    Forum Guru Kyle123's Avatar
    Join Date
    03-10-2010
    Location
    Leeds
    MS-Off Ver
    365 Win 11
    Posts
    7,238

    Re: VBA Code Using Arrays To Be Optimized

    It depends on how much data you have. At some point it becomes more efficient to sort and binary search rather than simply loop the data

  17. #17
    Valued Forum Contributor
    Join Date
    09-17-2012
    Location
    Johannesburg, South Africa
    MS-Off Ver
    Excel 2007
    Posts
    454

    Re: VBA Code Using Arrays To Be Optimized

    True. I have been thinking about a recursive binary attack, but I would avoid sorting.

  18. #18
    Forum Guru Kyle123's Avatar
    Join Date
    03-10-2010
    Location
    Leeds
    MS-Off Ver
    365 Win 11
    Posts
    7,238

    Re: VBA Code Using Arrays To Be Optimized

    Why would you do a binary search without sorting?

  19. #19
    Valued Forum Contributor
    Join Date
    09-17-2012
    Location
    Johannesburg, South Africa
    MS-Off Ver
    Excel 2007
    Posts
    454

    Re: VBA Code Using Arrays To Be Optimized

    Not quite a binary search. I'm attempting it now, better to post than explain.

  20. #20
    Valued Forum Contributor
    Join Date
    09-17-2012
    Location
    Johannesburg, South Africa
    MS-Off Ver
    Excel 2007
    Posts
    454

    Re: VBA Code Using Arrays To Be Optimized

    Ok, that was interesting. The recursive, divide and conquer method. Not very fast though.

    Option Explicit
    
    Sub BranchTest()
        Dim R As Variant
        R = Range("A1:B16")
        Range("E2").Value2 = RecurseConcat(R, "XXX0001", LBound(R, 1), UBound(R, 1))
    End Sub
    
    Private Function RecurseConcat(ByVal R As Variant, ByVal InputVal As String, ByVal R1 As Long, ByVal R2 As Long, Optional Codes As String = "#INIT#") As String
        Dim R0 As Long
        Dim Str As String
        Dim First As Boolean
        If Codes = "#INIT#" Then
            Codes = ""
            First = True
        End If
        If R1 = R2 Then
            If R(R1, 1) = InputVal Then
                Str = Codes & R(R1, 2)
            End If
        ElseIf (R2 - R1) = 1 Then
            Str = RecurseConcat(R, InputVal, R1, R1, Codes)
            Str = Str & RecurseConcat(R, InputVal, R2, R2, Codes)
        Else
            R0 = Int(((R2 - 1) - R1 + 1) * Rnd + R1)
            Str = RecurseConcat(R, InputVal, R1, R0, Codes)
            Str = Str & RecurseConcat(R, InputVal, R0 + 1, R2, Codes)
        End If
        If First Then
            Str = InputVal & " " & Mid(Replace(Str, "E", ",E"), 2) 'cheating I know
        End If
        RecurseConcat = Str
    End Function
    This is a similar technique to Quicksort if memory serves. But where Quicksort's goal is to sort, ours is to collect codes. Turns out this is a silly way to do it, but I'm glad I've tested it. Sometimes a different approach has an unexpected performance payoff, especially when recursion's involved.

  21. #21
    Forum Guru Kyle123's Avatar
    Join Date
    03-10-2010
    Location
    Leeds
    MS-Off Ver
    365 Win 11
    Posts
    7,238

    Re: VBA Code Using Arrays To Be Optimized

    That's very over engineered imo. Taking this back to the original question, there are two things to consider in improving the algorithm. The string method is the fastest, but there are 2 obvious ways to improve it depending on circumstance and how far you'd need to take it.

    1. If you need to look up multiple indexes at once, there comes a point where it's worth sorting the indexes. This allows for faster searching (binary), you don't need to iterate all the elements to get the full result. The catch is that the sorting takes time so is likely only worth it if you have a large amount of data and need to do multiple lookups.

    2. String building in vba/vb6 is inneficient since the string is recreated in its entirety on each operation, you can google string building class vb6 for a lower level more efficient implementation. Again for short strings the difference just won't be worth the effort, so this is really only worth doing if you expect the result string to be made up of a huge number of elements

    In summary, as tms said, you're unlikely to get any faster than looking up and buding the string whilst looping through. Whether it's worth the effort improving the algorithm very much depends on the circumstances, but unless you have a huge amount of data, I really wouldn't bother.

    To put this into context, with the sample data, the string building method executes 300,000 iterations in a third of a second.

  22. #22
    Valued Forum Contributor
    Join Date
    09-17-2012
    Location
    Johannesburg, South Africa
    MS-Off Ver
    Excel 2007
    Posts
    454

    Re: VBA Code Using Arrays To Be Optimized

    Yep, that's what I also concluded from my practical experiment. Although perfect, I don't consider 20/20 hindsight as a useful skill in developing innovative solutions.

  23. #23
    Forum Expert NeedForExcel's Avatar
    Join Date
    03-16-2013
    Location
    Pune, India
    MS-Off Ver
    Excel 2016:2019, MS 365
    Posts
    3,876

    Re: VBA Code Using Arrays To Be Optimized

    This thread was amazingly helpful to me..

    So basically I tried to measure the Run Time myself too..

    All 4 Codes do almost similar Run-Times, but these 2 (UsingArray & KyleLoops) are almost instantaneous..

    I used only 50K rows (Which isn't too much, but as much as I would normally require), so that Transpose wont cause issues.

    Sub UsingArray()
        
        Dim InputVal As String, R As Variant, Counter As Long, NewVal(), C As Long
        
        InputVal = Range("E1").Value2
        
        R = Range("A1").CurrentRegion.Value2
        
        ReDim NewVal(1 To UBound(R))
        
        For Counter = LBound(R) To UBound(R)
            If R(Counter, 1) = InputVal Then
                C = C + 1
                NewVal(C) = R(Counter, 2)
            End If
        Next Counter
        
        ReDim Preserve NewVal(1 To C)
        
        Range("F1").Value2 = InputVal & " " & Join(NewVal, ",")
        
    End Sub
    Sub KyleLoops()
    
    Dim InputVal As String
        Dim R As Variant, Counter As Long, Temp As String
        
        InputVal = Range("E4").Value2
    
        R = Range("A1").CurrentRegion.Value2
        
        For Counter = LBound(R) To UBound(R)
            If R(Counter, 1) = InputVal Then Temp = Temp & R(Counter, 2) & ","
        Next Counter
    
        Range("F4").Value2 = InputVal & " " & Left$(Temp, Len(Temp) - 1)
    
    End Sub

    Sub UsingDictionary()
    
        Dim Dict As Scripting.Dictionary, R As Variant, Counter As Long, InputVal As String
        Set Dict = New Scripting.Dictionary
        
        InputVal = Range("E2").Value2
        
        With Dict
            R = Application.Transpose(Range("A1:B" & Cells(Rows.Count, 1).End(xlUp).Row).Value2)
            
            For Counter = LBound(R, 2) To UBound(R, 2) Step 1
                If .Exists(R(1, Counter)) Then
                    .Item(R(1, Counter)) = .Item(R(1, Counter)) & "," & R(2, Counter)
                Else
                    .Add (R(1, Counter)), R(2, Counter)
                End If
            Next Counter
            Range("F2").Value2 = InputVal & " " & .Item(InputVal)
        End With
        
    End Sub
    Sub UsingLoops()
    
        Dim LR As Long, Counter As Long, InputVal As String, A
        
        LR = Cells(Rows.Count, 1).End(xlUp).Row
        
        InputVal = Range("E3").Value2
        
        For Counter = 1 To LR
            If Cells(Counter, 1).Value2 = InputVal Then
                A = A & "," & Cells(Counter, 2).Value2
            End If
        Next Counter
        
        Range("F3").Value2 = InputVal & " " & Right(A, Len(A) - 1)
    End Sub
    Attached Files Attached Files

+ Reply to Thread

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Similar Threads

  1. [SOLVED] Shorten Code with arrays
    By CraigsWorld in forum Excel Programming / VBA / Macros
    Replies: 3
    Last Post: 03-26-2015, 01:45 PM
  2. Curious about a more optimized method
    By cman0 in forum Excel Formulas & Functions
    Replies: 5
    Last Post: 11-24-2014, 07:40 PM
  3. Optimized Product Selection Problem
    By Atticus_Finch in forum Excel Programming / VBA / Macros
    Replies: 2
    Last Post: 11-09-2013, 09:33 AM
  4. Create an Optimized Schedule (Warning: A lot of reading!)
    By BYizz in forum Excel Programming / VBA / Macros
    Replies: 6
    Last Post: 09-03-2013, 02:48 PM
  5. Optimized cutting length help needed
    By mr63249 in forum Excel General
    Replies: 2
    Last Post: 05-31-2013, 08:06 AM
  6. [SOLVED] Very inefficient code because of different sizes of arrays, how do i shorten my code?
    By Brammer88 in forum Excel Programming / VBA / Macros
    Replies: 4
    Last Post: 12-28-2012, 04:49 PM
  7. Working Code: Can it be optimized?
    By qcity in forum Excel Programming / VBA / Macros
    Replies: 19
    Last Post: 06-17-2011, 01:00 AM

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts

Search Engine Friendly URLs by vBSEO 3.6.0 RC 1