Optimize "For Each" routine to fill cells

SY24

New Member
Joined
Oct 6, 2023
Messages
8
Office Version
  1. 2013
Platform
  1. Windows
Greetings,

we use semi-huge parts lists in our company and I'd like to highlight certain parts in yellow. While I do have working VBA code, it operates kinda slowly the bigger our files get (between 200 to 500 rows).

Here's an example:
Assembly.xlsx
ABCDE
1Superstructure Name
2LevelPos.Asm. No.Asm. NameAmount
31.........1Fan1,000
4.2........1Blades3,000
5.2........2Motor1,000
6.2........3Housing1,000
7.2........4Oscillator1,000
8.2........5Screws15,000
9.2........6Base1,000
10.2........7Switch1,000
11.2........8Power Chord1,000
12.2........9Grille1,000
Sheet1
VBA Code:
Dim rCell As Range

    For Each rCell In Range("D:D")
        If rCell.Value Like "*Motor*" Then
            rCell.Interior.ColorIndex = 6
        ElseIf rCell.Value Like "*Oscillator*" Then
            rCell.Interior.ColorIndex = 6
        ElseIf rCell.Value Like "*Switch*" Then
            rCell.Interior.ColorIndex = 6
        ElseIf rCell.Value Like "*Button Panel*" Then
            rCell.Interior.ColorIndex = 6

        End If
    Next
Assembly.xlsx
ABCDE
1Superstructure Name
2LevelPos.Asm. No.Asm. NameAmount
31.........1Fan1,000
4.2........1Blades3,000
5.2........2Motor1,000
6.2........3Housing1,000
7.2........4Oscillator1,000
8.2........5Screws15,000
9.2........6Base1,000
10.2........7Switch1,000
11.2........8Power Chord1,000
12.2........9Grille1,000
Sheet1

Not all options defined in the code get used in every parts list, but that's ok.

I read a lot of articles and forums for optimizing this routine (many saying that For Each is the slowest), but failed to understand how to implement their solutions to my code. So I am lookin for an efficient and faster operating routine that also lets me add new options whenever needed.

Any help in regards to the problem above is greatly appreciated!

SY24
 

Excel Facts

Bring active cell back into view
Start at A1 and select to A9999 while writing a formula, you can't see A1 anymore. Press Ctrl+Backspace to bring active cell into view.
At this point I can't see a beneficial use of dictionaries. maybe someone does.
My code considers your partial part list is in column G. You can change this address to wherever you want. The following code should be fast enough:
VBA Code:
Sub test()
  Dim myArray As Variant, parts As Variant, myRange As Range, part As Variant
  With Application
  myArray = .Transpose(Range("D1:D" & Cells(Rows.Count, "D").End(xlUp).Row))
  parts = .Transpose(Range("G1:G" & Cells(Rows.Count, "G").End(xlUp).Row))
  Set myRange = Range("XFD1048576") 'Just to set something to myRange
  For Each part In parts
    If Not IsError(.Match("*" & part & "*", myArray, 0)) Then
      Set myRange = .Union(myRange, Range("D" & .Match("*" & part & "*", myArray, 0)))
    End If
  Next
  
  .ScreenUpdating = False
  myRange.Interior.ColorIndex = 6
  Range("XFD1048576").Interior.ColorIndex = xlNone
  .ScreenUpdating = True
  End With
End Sub
 
Upvote 1
1235.xlsm
ABCDEFGHI
1Superstructure Name
2LevelPos.Asm. No.Asm. NameAmountList For Highlightes
31.........1Fan1Housing
4.2........1Blades3Base
5.2........2Motor1Grille
6.2........3Housing1
7.2........4Oscillator1
8.2........5Screws15
9.2........6Base1
10.2........7Switch1
11.2........8Power Chord1
12.2........9Grille1
Sheet13





VBA Code:
Sub HighlightMatchingValues()
    Dim ws As Worksheet
    Dim lastRowD As Long, lastRowF As Long
    Dim cellD As Range, cellF As Range

    ' Set the worksheet to work with
    Set ws = ThisWorkbook.Sheets("Sheet13") ' Replace "Sheet13" with your sheet name

    ' Clear previous highlights (remove background color) in column D
    ws.Range("D1:D" & ws.Cells(ws.Rows.Count, "D").End(xlUp).Row).Interior.ColorIndex = xlNone

    ' Find the last row in columns D and I
    lastRowD = ws.Cells(ws.Rows.Count, "D").End(xlUp).Row
    lastRowF = ws.Cells(ws.Rows.Count, "I").End(xlUp).Row

    ' Loop through each cell in column D
    For Each cellD In ws.Range("D3:D" & lastRowD)
        ' Loop through each cell in column F
        For Each cellF In ws.Range("I3:I" & lastRowF)
            ' Check if the value in column D exists in column F
            If cellD.Value = cellF.Value Then
                ' Highlight the cell in column D
                cellD.Interior.ColorIndex = 6 ' Change the color index as needed
                Exit For ' Exit the loop after the first match
            End If
        Next cellF
    Next cellD
End Sub



Change code according to your data range and sheet name
 
Upvote 1
How about using conditional formatting, or is that not an option for you?
Book1
ABCDE
1Superstructure Name
2LevelPos.Asm. No.Asm. NameAmount
31.........1Fan1
4.2........1Blades3
5.2........2Motor1
6.2........3Housing1
7.2........4Oscillator1
8.2........5Screws15
9.2........6Base1
10.2........7Switch1
11.2........8Power Chord1
12.2........9Grille1
Sheet1
Cells with Conditional Formatting
CellConditionCell FormatStop If True
D3:D12Expression=ISNUMBER(SEARCH("Button Panel",D3))textNO
D3:D12Expression=ISNUMBER(SEARCH("Switch",D3))textNO
D3:D12Expression=ISNUMBER(SEARCH("Oscillator",D3))textNO
D3:D12Expression=ISNUMBER(SEARCH("Motor",D3))textNO
 
Upvote 1
How about using conditional formatting, set via code?
VBA Code:
Option Explicit

Sub FormatThings()
Dim rng As Range
Dim arrValues As Variant
Dim idx As Long

    arrValues = Array("Switch", "Motor", "Oscillator")
    Set rng = Range("D3", Range("D" & Rows.Count).End(xlUp))

    With rng.FormatConditions
        .Delete
        For idx = LBound(arrValues) To UBound(arrValues)
            .Add Type:=xlTextString, String:=arrValues(idx), TextOperator:=xlContains

            With rng.FormatConditions(idx + 1).Interior
                .PatternColorIndex = xlAutomatic
                .Color = 65535
                .TintAndShade = 0
            End With
        Next idx
      
    End With

End Sub
 
Upvote 1
Another option to test for speed.

VBA Code:
Sub Check_Parts()
  Dim RX As Object
  Dim a As Variant, b As Variant
  Dim i As Long, k As Long

  Set RX = CreateObject("VBscript.RegExp")
  RX.Pattern = "Motor|Oscillator|Switch|Button Panel" '<- add more if required
  With Range("D3", Range("D" & Rows.Count).End(xlUp))
    a = .Value
    ReDim b(1 To UBound(a), 1 To 1)
    For i = 1 To UBound(a)
      If RX.test(a(i, 1)) Then
        b(i, 1) = 1
        k = 1
      End If
    Next i
    If k > 0 Then
      Application.ScreenUpdating = False
      .Value = b
      .SpecialCells(xlConstants).Interior.ColorIndex = 6
      .Value = a
      Application.ScreenUpdating = True
    End If
  End With
End Sub

@Georgiboy
To emulate the OP's case-sensitive code you would need to use FIND instead of SEARCH.
Also, in CF there would be no need for the ISNUMBER check since any error result will mean the CF test fails and the cell would not be coloured anyway.
 
Upvote 1
Solution
Also combination of VBA and conditional formatting, again using a named range on your sheet called "parts" for easy adding of new items. Tested at 100K rows at 0.02 secs.
VBA Code:
Option Explicit
Sub Highlight_If_Found()
    Dim ws As Worksheet
    Application.ScreenUpdating = False
    Set ws = Worksheets("Sheet1")       '<-- *** Change sheet name as required ***
    
    With ws.Range("D3", ws.Cells(Rows.Count, "D").End(xlUp))
        .FormatConditions.Delete
        .FormatConditions.Add Type:=xlExpression, _
        Formula1:="=NOT(ISERROR(MATCH(""*""&D3&""*"",parts,0)))"
        .FormatConditions(1).Interior.Color = vbYellow
    End With
    Application.ScreenUpdating = True
End Sub

Before:
test.xlsm
ABCDEFGH
1Superstructure Name
2LevelPos.Asm. No.Asm. NameAmountRange named "parts"
31.........1Fan1Motor
4.2........1Blades3Oscillator
5.2........2Motor1Switch
6.2........3Housing1Button Panel
7.2........4Oscillator1
8.2........5Screws15
9.2........6Base1
10.2........7Switch1
11.2........8Power Chord1
12.2........9Grille1
131.........1Fan1
14.2........1Blades3
15.2........2Motor1
16.2........3Housing1
17.2........4Oscillator1
18.2........5Screws15
19.2........6Base1
20.2........7Switch1
21.2........8Power Chord1
22.2........9Grille1
Sheet1


After
test.xlsm
ABCDEFGH
1Superstructure Name
2LevelPos.Asm. No.Asm. NameAmountRange named "parts"
31.........1Fan1Motor
4.2........1Blades3Oscillator
5.2........2Motor1Switch
6.2........3Housing1Button Panel
7.2........4Oscillator1
8.2........5Screws15
9.2........6Base1
10.2........7Switch1
11.2........8Power Chord1
12.2........9Grille1
131.........1Fan1
14.2........1Blades3
15.2........2Motor1
16.2........3Housing1
17.2........4Oscillator1
18.2........5Screws15
19.2........6Base1
20.2........7Switch1
21.2........8Power Chord1
22.2........9Grille1
Sheet1
Cells with Conditional Formatting
CellConditionCell FormatStop If True
D3:D100002Expression=NOT(ISERROR(MATCH("*"&D3&"*",parts,0)))textYES
 
Upvote 1
@Georgiboy
To emulate the OP's case-sensitive code you would need to use FIND instead of SEARCH.
Also, in CF there would be no need for the ISNUMBER check since any error result will mean the CF test fails and the cell would not be coloured anyway.
Yes sorry, posted first thing in the morning before my first black coffee.

Below for completeness:
Book1
ABCDE
1Superstructure Name
2LevelPos.Asm. No.Asm. NameAmount
31.........1Fan1
4.2........1Blades3
5.2........2Motor1
6.2........3Housing1
7.2........4Oscillator1
8.2........5Screws15
9.2........6Base1
10.2........7Switch1
11.2........8Power Chord1
12.2........9Grille1
Sheet1
Cells with Conditional Formatting
CellConditionCell FormatStop If True
D3:D12Expression=FIND("Button Panel",D3)textNO
D3:D12Expression=FIND("Switch",D3)textNO
D3:D12Expression=FIND("Oscillator",D3)textNO
D3:D12Expression=FIND("Motor",D3)textNO
 
Upvote 1
Thank you @Georgiboy and @Norie for your solutions. But I'm afraid I cannot use conditional formatting in my case. There are other operations being applied to our parts-lists. But I will definitely give your ideas a try!

Thank you @Muhammad_Usman and @Flashbond for your solutions and @kevin9999 for the combination with conditional formatting. Again, I'm not sure if that would fit into our process, but I will give it a try. Tho could I have this extra range you used on a different Excel file? And if so, how would I achieve that through code? That way my colleagues would be able to add entries by themselves which would come in handy.

Thank you @Peter_SSs for your solution. I will definitely give this a try. In our case we have around 100 options, and I fear the code would become harder to read the more options I add. Do you have an idea on how I could make this easier to read? And would this work with wildcards as well?
 
Upvote 0
And would this work with wildcards as well?
It already acts as if wildcards are included since that is what your original post 1 code did. Here is the results of my code with some different data.
Note the comments earlier in the thread about case-sensitivity though and that "oscillator" in cell D14 is not highlighted since your original 'working code' also would not highlight that cell. Is that what you want?

SY24_1.xlsm
D
2Asm. Name
3Fan
4Blades
5Motor
6Housing
7Oscillator
8Screws
9Base
10Switch Board Meter
11Power Chord
12Grille
13Electric Motor
14oscillator
15Blue Switches
Sheet1 (5)


I fear the code would become harder to read the more options I add. Do you have an idea on how I could make this easier to read?
There would be at least two options.

1. Write the Pattern line in a multi-line format like this
VBA Code:
 RX.Pattern = "Text1|Text2|Text3|Text4|Text5|Text6|Text7|Text8|Text9|Text10|Text11|" & _
                "Text12|Text13|Text14|Text15|Text16|Text17|Text18|Text19|Text20|" & _
                "Text21|Text22|Text23|Text24|Text25|Text26|Text27|Text28|Text29|Text30|" & _
                "Text31|Text32|Text33|Text34|Text35"

2. Write the texts of interest in a column somewhere (as suggested earlier in the thread) ..

SY24_1.xlsm
J
1
2Text1
3Text2
4Text3
5Text4
6Text5
7Text6
8Text7
9Text8
10Text9
11Text10
12Text11
13Text12
14Text13
15Text14
16Text15
17Text16
18Text17
19Text18
20Text19
21Text20
22Text21
23Text22
24Text23
25Text24
26Text25
27Text26
28Text27
29Text28
30Text29
31Text30
32Text31
33Text32
34Text33
35Text34
36Text35
Sheet1 (5)


.. and write the Pattern line like this

VBA Code:
RX.Pattern = Join(Application.Transpose(Range("J2", Range("J" & Rows.Count).End(xlUp)).Value), "|")
 
Upvote 1

Forum statistics

Threads
1,215,140
Messages
6,123,266
Members
449,093
Latest member
Vincent Khandagale

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