VBA Help - Speed up a For Each Statement

Johnny Thunder

Well-known Member
Joined
Apr 9, 2010
Messages
693
Office Version
  1. 2016
Platform
  1. MacOS
Hello all,

I have a For Each statement piece of code that adds a value to a column at the end of my report. My current code is processing 30k rows in excel and runs in 40 seconds. I was hoping someone may see a better way to write the code I have in a more efficient way to speed up the run time.

Any ideas are appreciated.

Here is the code:

Code:
'----------------------------------------------------------------------------------------
'--- Using the Group List by Titles tag each record with Unique Name
'----------------------------------------------------------------------------------------
Sub GroupTitles()


Dim ws1 As Worksheet, ws2 As Worksheet
Dim Count As String, OriginalTitle As String, GroupTitle As String
Dim ceLL As Range, ceLL2 As Range
Dim LastR1 As Long, LastR2 As Long

Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.Calculation = xlCalculationManual



Set ws1 = Sheets("Download")
Set ws2 = Sheets("Lookup")


LastR1 = ws1.Range("G" & Rows.Count).End(xlUp).Row
LastR2 = ws2.Range("B" & Rows.Count).End(xlUp).Row


'ws1.Range("O2:O" & LastR1).ClearContents   'Clears the Group column First


For Each ceLL In ws2.Range("B2:B" & LastR2) 'Loops thru the list of Grouped Titles
    If ceLL.Value <> "" Then
        OriginalTitle = ceLL.Value
        GroupTitle = ceLL.Offset(0, 1).Value
    End If


        For Each ceLL2 In ws1.Range("B2:B" & LastR1)    'Loops down the download sheet Column B
            If ceLL2.Value = OriginalTitle Then
                ceLL2.Offset(0, 13).Value = GroupTitle
            Else: ceLL2.Offset(0, 13).Value = ceLL2.Value   'Brings in regular titles into the Grouped Column
            End If
        Next ceLL2


Next ceLL

Application.ScreenUpdating = True
Application.DisplayAlerts = True
Application.Calculation = xlCalculationAutomatic

        
End Sub
 
Last edited:

Excel Facts

Select a hidden cell
Somehide hide payroll data in column G? Press F5. Type G1. Enter. Look in formula bar while you arrow down through G.
Try the following macro.
Maybe it can be improved if you put a sample with 10 or 15 records per sheet and the result you expect. That way I can understand the process and seek an improvement.


Code:
Sub GroupTitles()
  Dim ws1 As Worksheet, ws2 As Worksheet, OriginalTitle As String, GroupTitle As String
  Dim a() As Variant, b() As Variant, c() As Variant, i As Long, j As Long
  Application.ScreenUpdating = False
  Application.Calculation = xlCalculationManual
  Set ws1 = Sheets("Download")
  Set ws2 = Sheets("Lookup")
  a = ws1.Range("B2:B" & ws1.Range("B" & Rows.Count).End(xlUp).Row).Value
  b = ws2.Range("B2:C" & ws2.Range("B" & Rows.Count).End(xlUp).Row).Value
  ReDim c(1 To UBound(a), 1 To 1)
  For i = 1 To UBound(b, 1)
    If b(i, 1) <> "" Then
      OriginalTitle = b(i, 1)
      GroupTitle = b(i, 2)
    End If
    For j = 1 To UBound(a, 1)
      If a(j, 1) = OriginalTitle Then
        c(j, 1) = GroupTitle
      Else
        c(j, 1) = a(j, 1)
      End If
    Next
  Next
  Range("O2").Resize(UBound(a)).Value = c()
  Application.Calculation = xlCalculationAutomatic
End Sub
 
Upvote 0
Hi Dante,

Thank you for the code you provided.

So this definitely looks waaaaay better than what I came up with. I am having a small issue though that you might know how to fix.

So, when I run the code as is, it seems to not populate the "GroupTitles" Part, but it does do the line Else c(j,1) = a(j,1).

So, I tried actually doing the opposite, I commented out the Else statement and then the code does exactly what I need it to do. I have a funny feeling it may be re-looping the range that it tags with "GroupTitle"

Any Ideas on how to fix? I did clean up the spacing of the code so its easier on the eyes.

Code:
Sub GroupTitles()
  
Dim ws1 As Worksheet, ws2 As Worksheet, OriginalTitle As String, GroupTitle As String
Dim a() As Variant, b() As Variant, c() As Variant, i As Long, j As Long


Application.ScreenUpdating = False
  
Set ws1 = Sheets("Download")
Set ws2 = Sheets("Lookup")


a = ws1.Range("B2:B" & ws1.Range("B" & Rows.Count).End(xlUp).Row).Value
b = ws2.Range("B2:C" & ws2.Range("B" & Rows.Count).End(xlUp).Row).Value
  
        ReDim c(1 To UBound(a), 1 To 1)
            For i = 1 To UBound(b, 1)
                If b(i, 1) <> "" Then
                    OriginalTitle = b(i, 1)
                    GroupTitle = b(i, 2)
                End If
        
        For j = 1 To UBound(a, 1)
            If a(j, 1) = OriginalTitle Then
                c(j, 1) = GroupTitle
            Else
                c(j, 1) = a(j, 1)
            End If
        Next
            Next
        
        Range("O2").Resize(UBound(a)).Value = c()
        
      
        Application.ScreenUpdating = True
        
        MsgBox "All Done"


End Sub
 
Upvote 0
So, when I run the code as is, it seems to not populate the "GroupTitles" Part, but it does do the line Else c(j,1) = a(j,1).

So, I tried actually doing the opposite, I commented out the Else statement and then the code does exactly what I need it to do. I have a funny feeling it may be re-looping the range that it tags with "GroupTitle"

Any Ideas on how to fix? I did clean up the spacing of the code so its easier on the eyes.

As I told you to improve the code, I need to understand what you need. And for that you must explain with examples of what you have in your sheets and the expected result.
 
Upvote 0
So, when I run the code as is, it seems to not populate the "GroupTitles" Part, but it does do the line Else c(j,1) = a(j,1).
That isn't surprising since that is exactly what your code from post 1 does too & Dante was just giving you a faster way, which is what you asked for. ;)

This is my suggestion for what I think you want.

Rich (BB code):
Sub Group_Titles()
  Dim d As Object
  Dim ws1 As Worksheet, ws2 As Worksheet
  Dim a As Variant
  Dim i As Long
  
  Set d = CreateObject("Scripting.Dictionary")
  d.CompareMode = 1
  Set ws1 = Sheets("Download")
  Set ws2 = Sheets("Lookup")
  With ws2
    a = .Range("B2", .Range("B" & .Rows.Count).End(xlUp)).Resize(, 2).Value
  End With
  For i = 1 To UBound(a)
    If Len(a(i, 1)) > 0 Then d(a(i, 1)) = a(i, 2)
  Next i
  With ws1
    a = .Range("B2", .Range("B" & .Rows.Count).End(xlUp)).Value
    For i = 1 To UBound(a)
      If d.exists(a(i, 1)) Then a(i, 1) = d(a(i, 1))
    Next i
    .Range("O2").Resize(UBound(a)).Value = a
  End With
End Sub
 
Last edited:
Upvote 0
Hi Peter,

Thanks for chiming in. You proposal looks great but unfortunately the scripting dictionary doesn't seem to work on Excel for Mac, is there a way to modify that bit to make it work for Mac?
 
Upvote 0
.. unfortunately the scripting dictionary doesn't seem to work on Excel for Mac, ..
Suggest that you always include that information in your first post, or thread title, or signature as the more common happening here is that if unstated, we tend to assume a Windows operating system. :)

In that case, I would like to know
- if the same value can occur more than once in column B of the 'Lookup' sheet? (If 'Yes' I might have more questions about that)
- if the same value can occur more than once in column B of the 'Download' sheet?
 
Last edited:
Upvote 0
Peter/Dante


I was able to achieve what I needed with a small change to Dante's code (see below)

It seems like the code was re-tagging values that it had already previously tagged so by adding a "if the cell is blank" statement then proceed if not then do nothing and it take care of the issue.

Code:
Sub GroupTitles()
  
Dim ws1 As Worksheet, ws2 As Worksheet, OriginalTitle As String, GroupTitle As String
Dim a() As Variant, b() As Variant, c() As Variant, i As Long, j As Long
 
Set ws1 = Sheets("Download")
Set ws2 = Sheets("Lookup")


Application.ScreenUpdating = False


a = ws1.Range("B2:B" & ws1.Range("B" & Rows.Count).End(xlUp).Row).Value
b = ws2.Range("B2:C" & ws2.Range("B" & Rows.Count).End(xlUp).Row).Value
  
        ReDim c(1 To UBound(a), 1 To 1)
            For i = 1 To UBound(b, 1)
                If b(i, 1) <> "" Then
                    OriginalTitle = b(i, 1)
                    GroupTitle = b(i, 2)
                End If
        
        For j = 1 To UBound(a, 1)
            If a(j, 1) = OriginalTitle Then
                c(j, 1) = GroupTitle
            ElseIf c(j, 1) = "" Then c(j, 1) = a(j, 1)
            End If
        Next
            Next
        
        Range("O2").Resize(UBound(a)).Value = c()
        
MsgBox Prompt:="Updates have been processed", Title:="FINITO!"
              
    End Sub
 
Upvote 0
Peter/Dante


I was able to achieve what I needed with a small change to Dante's code (see below)

It seems like the code was re-tagging values that it had already previously tagged so by adding a "if the cell is blank" statement then proceed if not then do nothing and it take care of the issue.


The problem of overwriting labels is a problem from your original code. I only transcribed your code, including "errors".


I had to do that code transcript because I don't have the context of your data nor do I have examples of what you need.

If you could give examples of your sheets, I could review the code and improve it.

Initially, I commented to you that the second for is not necessary, that would save a lot of time, but without examples I am blind.
 
Upvote 0

Forum statistics

Threads
1,223,894
Messages
6,175,252
Members
452,623
Latest member
Techenthusiast

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top