I have created a large spreadsheet with around 80,000 cells with an array of conditional formatting highlighting different points of interest. Two of these conditional formats involve large arrays that cause slowdown on the file. I'm looking for a way to make this more efficient.
The table reads left to right with the dates Jan 1st to Dec 31st along the top and down with various items and the limits identified in their respective row on the far right after Dec 31st. The date ordering prevents sorting of the data beforehand.
Below is the initial root for the formula. This is then replicated to both solve the problem and cause the problem. It looks through the unsorted row for non-blank cells, picks the top X number of values and compares them to the limit in Col $NG. It ignores the description columns with the -5 row reference and provides a 0 to avoid a #NUM error for insufficient values for LARGE().
IF(ISERROR(INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),1)))-5)>$NG2),0,INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),1)))-5)>$NG2)
This is the first of 2 conditional formatting formulas that aims to highlight the row when 2 out of 3 of the most recent (furthest right) values are above the limit in $NG2.
=IF(COUNT($F2:$NF2)<2,"",SUM(IF(ISERROR(INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),1)))-5)>$NG2),0,INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),1)))-5)>$NG2),IF(ISERROR(INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),2)))-5)>$NG2),0,INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),2)))-5)>$NG2),IF(ISERROR(INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),3)))-5)>$NG2),0,INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),3)))-5)>$NG2))>=2)
Again, a replication of the root formula 10 times to catch 3 out of 10 of the furthest right values being above the limit.
=IF(COUNT($F2:$NF2)<2,"",SUM(IF(ISERROR(INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),1)))-5)>$NG2),0,INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),1)))-5)>$NG2),IF(ISERROR(INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),2)))-5)>$NG2),0,INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),2)))-5)>$NG2),IF(ISERROR(INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),3)))-5)>$NG2),0,INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),3)))-5)>$NG2),IF(ISERROR(INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),4)))-5)>$NG2),0,INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),4)))-5)>$NG2),IF(ISERROR(INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),5)))-5)>$NG2),0,INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),5)))-5)>$NG2),IF(ISERROR(INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),6)))-5)>$NG2),0,INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),6)))-5)>$NG2),IF(ISERROR(INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),7)))-5)>$NG2),0,INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),7)))-5)>$NG2),IF(ISERROR(INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),8)))-5)>$NG2),0,INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),8)))-5)>$NG2),IF(ISERROR(INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),9)))-5)>$NG2),0,INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),9)))-5)>$NG2),IF(ISERROR(INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),10)))-5)>$NG2),0,INDEX($F2:$NF2,SUMPRODUCT((LARGE(($F2:$NF2<>"")*COLUMN($F2:$NF2),10)))-5)>$NG2))>=3)
I considered an xlToLeft VBA Function with a Do Until 2 Or 3, but struggled to replicate the variable column ID within the Index Match.
In case it's useful to anyone else as a reference:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim a, w, x, y, z As Integer
Dim b As Range
w = 0
x = Target.Row()
'Bypass limits column
y = Range("NG" & x).Column()
'Set column value while bypassing blanks
If Cells(x, y - 1) <> "" Then
y = y - 1
Else
y = Cells(x, y).End(xlToLeft).Column
End If
z = 0
a = ActiveSheet.Cells(ActiveSheet.Rows.Count, "A").End(xlUp).Row
Set b = Range("F2:NF" & a)
If Not Target.Cells.Count > 1 Then
If Not Application.Intersect(b, Range(Target.Address)) Is Nothing Then
Do Until y = 5 Or z = 2 Or w >= 3
If Cells(x, y) >= 0 Then
If Cells(x, y) > Range("NG" & x) Then
z = z + 1
End If
End If
w = w + 1
If Cells(x, y - 1) <> "" Then
y = y - 1
Else
y = Cells(x, y).End(xlToLeft).Column
End If
Loop
If z < 2 And y > 5 Then
Do Until y = 5 Or z = 3 Or w >= 10
If Cells(x, y) >= 0 Then
If Cells(x, y) > Range("NG" & x) Then
z = z + 1
End If
End If
w = w + 1
If Cells(x, y - 1) <> "" Then
y = y - 1
Else
y = Cells(x, y).End(xlToLeft).Column
End If
Loop
End If
If z = 2 And w <= 3 Then
With Range("A" & x, "E" & x).Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = 12874308
.TintAndShade = 0
.PatternTintAndShade = 0
End With
With Range("A" & x, "E" & x).Font
.Color = -2
.TintAndShade = 0
End With
Range("A" & x, "E" & x).Font.Bold = True
End If
If z = 3 And w >= 3 Then
With Range("A" & x, "E" & x).Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = 7434613
.TintAndShade = 0
.PatternTintAndShade = 0
End With
With Range("A" & x, "E" & x).Font
.Color = -13533715
.TintAndShade = 0
End With
Range("A" & x, "E" & x).Font.Bold = True
End If
If z <= 2 And w > 3 Then
If Range("A" & x).Font.Bold = True Then
With Range("A" & x, "E" & x).Font
.ColorIndex = xlAutomatic
.TintAndShade = 0
End With
With Range("A" & x, "E" & x).Interior
.Pattern = xlNone
.TintAndShade = 0
.PatternTintAndShade = 0
End With
Range("A" & x, "E" & x).Font.Bold = False
End If
End If
End If
End If
End Sub
I am trying to highlight top 2 values for each row for visible cells only using conditional formatting in Excel macro. My range is dynamic, hence I am running a loop to arrive at the last cell of the range.
Here is my code:
With Sheets("pcSupplyChainAnalysis").Select
For i = 2 To ctr
Set rng = Range("C" & i & ":" & "I" & i).SpecialCells(xlCellTypeVisible)
rng.FormatConditions.AddTop10
rng.FormatConditions(rng.FormatConditions.Count).SetFirstPriority
With rng.FormatConditions(1)
.TopBottom = xlTop10Top
.Rank = 2
.Percent = False
End With
With rng.FormatConditions(1).Interior
.PatternColorIndex = xlAutomatic
.Color = 255
.TintAndShade = 0
End With
rng.FormatConditions(1).StopIfTrue = False
Next
End With
Ctr is a counter I am running to find the position of the last non blank cell, as my data has blank values too and I am copying it from another sheet using macro.
ctr = 2
Do While (ActiveSheet.Range("A" & ctr).Value <> "")
ctr = ctr + 1
Loop
ctr = ctr - 1
ActiveSheet.Range("B2:I" & ctr).Select
Selection.Cut
Range("C2:J" & ctr).Select
ActiveSheet.Paste
Attached is the image of the format of my data. I want to highlight top 2 numbers for each row and ONLY FOR VISIBLE CELLS (as I am using some filters also in the range).
Try this:
Option Explicit
Public Sub ShowTop2()
Dim rng As Range, visibleRow As Range
Application.ScreenUpdating = False
With ThisWorkbook.Worksheets("pcSupplyChainAnalysis")
.Columns.FormatConditions.Delete
Set rng = .UsedRange.SpecialCells(xlCellTypeVisible)
End With
For Each visibleRow In rng.Rows
If visibleRow.Row > 1 Then
With visibleRow.FormatConditions
.AddTop10
.Item(.Count).SetFirstPriority
With .Item(1)
.TopBottom = xlTop10Top
.Rank = 2
.Interior.Color = 255
End With
End With
End If
Next
Application.ScreenUpdating = True
End Sub
An easier way to determine the last used row in column A:
ctr = Worksheets("pcSupplyChainAnalysis").Cells(Rows.Count, "A").End(xlUp).Row
You don't need to Select anything for any of your actions
I have an issue with my VBA code. I try to compare 2 columns, both A and B columns. If some data match, for example let's say that A2 contains text in B3, then I need to compare the cell C2 with the column D. I don't understand why but I get the error "End If without block If". Thanks a lot for you help guys.
Here is my code :
Sub Compare()
For i = 1 To 100
For j = 1 To 50
If InStr(1, ActiveSheet.Cells(i, 1).Value, ActiveSheet.Cells(j, 2).Value, vbTextCompare) <> 0 _
Then For k = 1 To 20
If InStr(1, ActiveSheet.Cells(i, 3).Value, ActiveSheet.Cells(k, 4).Value, vbTextCompare) <> 0 Then MsgBox i
End If
Next k
End If
Next j
Next i
End Sub
I found the structure of your if statements a bit confusing and I'm not entirely sure you can do a for loop as a one-liner like that to get rid of all the end ifs. For what it's worth, I think this code is a bit easier to follow:
Sub Compare()
For i = 1 To 100
For j = 1 To 50
If InStr(1, ActiveSheet.Cells(i, 1).Value, ActiveSheet.Cells(j, 2).Value, vbTextCompare) <> 0 Then
For k = 1 To 20
If InStr(1, ActiveSheet.Cells(i, 3).Value, ActiveSheet.Cells(k, 4).Value, vbTextCompare) <> 0 Then MsgBox i
Next k
End If
Next j
Next i
End Sub
This runs w/o a compile error, but can't comment if it does what you want it to do.
sous2817 raised an interesting question in their answer about whether or not a 1-line statement works if the body of the if statement is itself a for-loop. The answer appears to be "no" -- unless the for-loop itself is squeezed onto one line by using the colon statement separator:
Sub test1() 'compile error
Dim i As Long, s As Long
If i = 0 _
Then For i = 1 To 10
s = s + i
Next i
MsgBox s
End Sub
Sub test2() 'compiles okay
Dim i As Long, s As Long
If i = 0 _
Then For i = 1 To 10: s = s + i: Next i
MsgBox s
End Sub
If statements on one line don't need the End If statement.
End If without block If
Sub comparison()
For i = 2 To 1000
For j = 2 To 1000
If Worksheets(Worksheet).Range("A" & i).Value = Worksheets(Worksheet).Range("L" & j).Value Then
Worksheets(worksheet).Range("N" & j).Select
With Selection.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = 65535
.TintAndShade = 0
.PatternTintAndShade = 0
End With
End If
Next j
Next i
End Sub
This question already has an answer here:
VBA Conditional Formatting if Cell Not Between
(1 answer)
Closed 7 years ago.
I have this VBA code for conditional formatting.
Selection.FormatConditions.Add Type:=xlCellValue, Operator:=xlNotBetween, _
Formula1:="=$I$10", Formula2:="=$J$10"
Selection.FormatConditions(Selection.FormatConditions.Count).SetFirstPriority
With Selection.FormatConditions(1).Interior
.PatternColorIndex = xlAutomatic
.Color = 255
.TintAndShade = 0
End With
Selection.FormatConditions(1).StopIfTrue = False
End Sub
I have a vbscript that creates an Excel sheet and I need to apply that VBA code to a cell in the Excel sheet as it is created. Having issues getting it to run. I know I need to sub the actual values for the excel constants but there's more to it I just don't get
What I've done so far
priceRange = "K"&rowNum + 2
objWorkSheet.Range(priceRange).FormatConditions.Add Type:=1, Operator:=2, Formula1:="=$I$"&finalRowNum + 1&"", Formula2:="=$J$"&finalRowNum + 1&""
objWorkSheet.Range(priceRange).FormatConditions(objExcel.Selection.FormatConditions.Count).SetFirstPriority
objWorkSheet.Range(priceRange).FormatConditions(1).Interior.PatternColorIndex = -4105
objWorkSheet.Range(priceRange).FormatConditions(1).Interior.Color = 255
objWorkSheet.Range(priceRange).FormatConditions(1).Interior.TintAndShade = 0
objWorkSheet.Range(priceRange).FormatConditions(1).StopIfTrue = False
I need it to apply the conditional formatting to the a specific cell (the one I defined as priceRange)
Untested:
Dim rng, fc, rowNum, finalRowNum, objWorkSheet
'...
'...
Set rng = objWorkSheet.Range("K" & rowNum + 2)
'vbscript doesn't support named arguments, only positional
Set fc = rng.FormatConditions.Add(1, 2, _
"=$I$" & finalRowNum, _
"=$J$" & finalRowNum)
fc.SetFirstPriority
With fc.Interior
.PatternColorIndex = -4105
.Color = 255
.TintAndShade = 0
End With
fc.StopIfTrue = False
I want to loop through 5 cells, Q5 - U5.
With each cell I want to check if the value is equal to "Y", and if yes, highlight the cell to make it green.
How may I do so? Can't seem to figure it out.
For Each c In Range("Q5:U5").Cells
c.Select
If c.Value = Y Then
With Selection.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = 5287936
.TintAndShade = 0
.PatternTintAndShade = 0
End With
End If
Next
You should try to avoid selecting/activating ranges: in 99% of cases there is no need (although the macro recorder always suggests otherwise)
For Each c In ActiveSheet.Range("Q5:U5").Cells
If c.Value = "Y" Then
With c.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = 5287936
.TintAndShade = 0
.PatternTintAndShade = 0
End With
End If
Next
When you don't define c as a range, the statement
For Each c in ActiveSheet.Range("Q5:U5").Cells
while valid, will actually result in c having the value of each of the cells. To solve this problem, declare the type explicitly:
Dim c as Range
Next, when you do the comparison (as already pointed out), use
If c.Value = "Y"
Note - if you declare
Option Compare Text
right at the top of your module, the comparison will be case-insensitive; otherwise, a "Y" will not match a "y".
The whole module would look like this, then:
Option Explicit
Option Compare Text
Sub colorMe()
Dim c as Range
For Each c In Range("Q5:U5").Cells
c.Select
If c.Value = "Y" Then
With Selection.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = 5287936
.TintAndShade = 0
.PatternTintAndShade = 0
End With
End If
Next
End Sub
I am sure it doesn't need to be pointed out that you could achieve the same thing with conditional formatting...
In your code, Y appears to be an undefined variable. To check for the value, put it in double quotes:
If c.Value = "Y" Then