Highlight cells based on row number on VBA - vba

I want to change the cell style based on the row number. I am still new on VBA.
Here is my code:
Sub format()
FinalRow = Cells(Rows.Count, 1).End(xlUp).Row
For i = 1 To FinalRow
If Rows.Count = 2 * i + 1 Then
Selection.Style = "Good"
ElseIf Rows.Count = 2 * i Then
Selection.Style = "Bad"
End If
ActiveCell.Offset(1, 0).Select
Next i
End Sub
The loop moves to the next cell but does not highlight if a criteria is met. May you please help me.

I suggest the following:
Option Explicit
Public Sub FormatEvenOddRowNumbers()
Dim FinalRow As Long
FinalRow = Cells(Rows.Count, 1).End(xlUp).Row
Dim i As Long
For i = 1 To FinalRow
If i Mod 2 = 0 Then 'even row number
Cells(i, 1).Style = "Good"
Else 'odd row number
Cells(i, 1).Style = "Bad"
End If
Next i
End Sub
To test if a row number is "even" you can use If i Mod 2 = 0 Then also you don't need to test for "odd" because if it is not "even" it must be "odd" so you can just use Else without any criteria.
Try to avoid using .Select it makes your code slow. See How to avoid using Select in Excel VBA. Instead access the cells directly like Cells(row, column).

First, I think you missused Rows.Count.
Rows.Count returns the total number of rows of your sheet. So now your criteria is only highlighting the two rows that are in the middle of the sheet.
If I assume correctly that you want to put "Good" the Rows that are even and "bad" the ones that are odds. then you should change your code to something like this:
Sub format()
FinalRow = Cells(Rows.Count, 1).End(xlUp).Row
For i = 1 To FinalRow
If i/2 = int(i/2) Then
Selection.Style = "Good"
ElseIf (i+1)/2 = int((i+1)/2) Then
Selection.Style = "Bad"
End If
ActiveCell.Offset(1, 0).Select
Next i
End Sub

Related

Conditional Subtraction in VBA

Looking for some assistance programming a report. I'm in the early stages. I've hit a wall when attempting to conditionally subtract using VBA. I would like to Subtract 1 from Column C if Column B is greater than 1. Any assistance would be greatly appreciated. The code I have so far is below
Sub UniqueContactReport()
Columns("Z:AQ").EntireColumn.Delete
Columns("X").EntireColumn.Delete
Columns("V").EntireColumn.Delete
Columns("U").EntireColumn.Delete
Columns("J:S").EntireColumn.Delete
Columns("A:H").EntireColumn.Delete
Dim N As Long, i As Long
N = Cells(Rows.Count, "B").End(xlUp).Row
For i = N To 1 Step -1
If Cells(i, "B") > 1 And Cells(i, "D") = 0 Then
Cells(i, "B").EntireRow.Delete
End If
Next i
End Sub
To succinctly address your question:
Sub ModifyColumnC()
Dim N As Long, i As Long
N = Cells(Rows.Count, "B").End(xlUp).Row ' See comment below
For i = 1 to N 'no need to go backwards because you are not deleting
If Cells(i, "B").Value > 1 Then
Cells(i, "C").Value = Cells(i,"C").Value -1
End If
Next i
End Sub
I have added .Value simply because I try to avoid implicit code. But yes, .Value is the default behaviour. I have left the code that determines the end row because all the following rows are going to be blank (thus <1) and this saves processing time (potentially a lot of it).
An alternative piece of code to do exactly the same thing.
Sub ModifyColumnC()
Dim N As Long, i As Long
N = Cells(Rows.Count, "B").End(xlUp).Row
For i = 1 to N
Cells(i, "C").Value = Cells(i,"C").Value - IIf(Cells(i, "B").Value > 1,1,0)
Next i
End Sub
A nuance is that in the IIf command, all paths are evaluated. But in this case, both the true and false statements are simple constants and unlikely to raise any exceptions.
From your question it seems this is what you want.
Sub UniqueContactReport()
Columns("Z:AQ").EntireColumn.Delete
Columns("X").EntireColumn.Delete
Columns("V").EntireColumn.Delete
Columns("U").EntireColumn.Delete
Columns("J:S").EntireColumn.Delete
Columns("A:H").EntireColumn.Delete
Dim N As Long, i As Long
N = Cells(Rows.Count, "B").End(xlUp).Row
For i = N To 1 Step -1
If Cells(i, "B") > 1 And Cells(i, "D") = 0 Then
debug.print Cells(i, "C").value - 1
End If
Next i
End Sub
you could make no loop and use AutoFilter():
Sub UniqueContactReport()
With Range("B1:B" & Cells(Rows.Count, "B").End(xlUp).Row) 'reference column B cells from row 1 down to last not empty one
.AutoFilter field:=1, Criteria1:=">1" ' filter referenced range on their value being >1
If Application.WorksheetFunction.Subtotal(103, .Cells) > 1 Then ' if any filtered cell other then the header (first one)
With .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).Offset(, 1) ' reference filtered cells corresponding ones one colum to the right
.Value = .Value - 1 ' lower referenced cell value by one
End With
If .Cells(1, 1).Value > 1 Then .Cells(1, 2) = .cell(1, 2) - 1 ' check if first cell is to be treated, too (first cell of filtered range is assumed as the "header" so doesn't get caught in filter action)
End If
.Parent.AutoFilterMode = False ' remove autofilter
End With
End Sub
BTW you can collapse all those EntireColumn.Delete statements into one:
Range("Z1:AQ1,X1, V1, U1, J:S, A:H").EntireColumn.Delete
but in this case you have to properly adjust the columns reference in the list since columns get deleted in one shot i.e. what-you-list-is-what-gets-deleted while in your previous approach the order of the delete statements affects which original columns gets actually deleted

Turn flag on/off to change name

I need help.
I have words and numbers in column A3:A500
and I need to change their names.
if a cell contains the word "previ" than put in a new column the letter "p" if the cells is a number. if its a word then dont put "p"
...like turning a flag on and off.
This is what i have:
Sub()
For i=3 to 500
x= range("a:"&i).value
If x contains "previ" Then
prevflag=1
ElseIf x is not integer Then
prevflag=0
End If
If prevflag=1 Then
range("H:"& i )= "p"
End If
Next i
End Sub
Can you guys help me make this work?
and thank you!!
this is what it needs to look like
https://postimg.org/image/e62z4xwlj/
Looking at your example, it looks like you want to put the "p" in rows in a section with a header that contains "previ" but not in a section with a header that doesn't. You also seem to want "p" in rows which have a blank in column A, not just integers. Does the below work for you?
Public Sub addPs()
Dim previFlag As Boolean
Dim c As Range: For Each c In Range("a1:a51")
If InStr(c.Value, "previ") > 0 Then
previFlag = True
ElseIf Not IsNumeric(c.Value) Then
previFlag = False
End If
If IsNumeric(c.Value) Then
If Int(c.Value) = c.Value And previFlag Then c.Offset(0, 3) = "p"
End If
Next c
End Sub
you may be after something like this
Option Explicit
Sub main()
Dim iRow As Long, lastRow As Long
lastRow = Cells(Rows.Count, "A").End(xlUp).row
iRow = 3
Do
If InStr(Cells(iRow, 1).Value, "previ") > 0 Then '<--| if current cell contains "previ
iRow = iRow + 1 '<--| then then start scanning for numeric values
Do
If IsNumeric(Cells(iRow, 1).Value) Then Cells(iRow, 3).Value = "p" '<--| if current cell is numeric then write "p" two cells left of it
iRow = iRow + 1
Loop While InStr(Cells(iRow, 1).Value, "Type") = 0 And iRow <= lastRow
Else
iRow = iRow + 1 '<--| else skip to next row
End If
Loop While iRow <= lastRow
End Sub
just change the column offset to your needs (you wrote column "H" but your example has "p"s in column "C")
I did not understand the cases, but still, this is how you check for numeric values:
?isnumeric(6)
True
?isnumeric("test")
False
In your code:
else if not isnumeric(x) then
Does this need to be done with VBA? You could put this formula in H3 and paste it down to H500:
=IF(ISERROR(FIND("previ",A3)),"","p")
However, this doesnt deal with your number criteria, but I don't know what you mean by that. If a cell contains "previ", that cell is not numeric. It may have some numeric digits in it somewhere, but "previ04578" is not a number. Could you share some sample data? Failing that you can check for any numeric digit with stacked substitutions and a length comparison, for example:
=IF(ISERROR(FIND("previ",A3)),"",IF(LEN(A1)=LEN(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(A3,"0",""),"9",""),"8",""),"7",""),"6",""),"5",""),"4",""),"3",""),"2",""),"1","")),"p",""))
Another alternative...
Sub FlagRows()
Dim i As Long, val As Variant, bFlag As Boolean: bFlag = False
With Sheets("Sheet1")
For i = 1 To 500
val = .Cells(i, 1).Value
bFlag = IIf(Not IsNumeric(val), IIf(InStr(CStr(val), "previ"), True, False), bFlag)
If IsNumeric(val) And bFlag = True Then .Cells(i, 4).Value = "p"
Next i
End With
End Sub

How to have selected columns (based on column header) fill down to the last row of the sheet?

I am writing a script to fill down values of the column based on the column header/first value of the column. In this case, I want the script to identify all columns with the header "||" and fill down to the current region of the sheet.
I have the following but instead of filling down on all columns I want just columns with "||" in the header. Can a condition be a added in thewith statements? Or is there a better approach?
Sub FillCellsFromAbove()
Option Explicit
On Error Resume Next
With Columns
.SpecialCells(xlCellTypeBlanks).formula = "=R[-1]C"
.Value = .Value
End With
Err.Clear
End Sub
Screenshot of spreadsheet: Imgur: The most awesome images on the Internet
Also, the || columns varies. Sometimes there could be 3, other times it could be 6+
Sub FillCellsFromAbove()
Dim lColumn As Long, yes As Integer
lColumn = Cells(1, Columns.Count).End(xlToLeft).Column
For i = 1 To lColumn
yes = InStr(Cells(1, i).Value, "||")
If yes <> "0" Then
'Add code to fill in the column
End If
Next i
End Sub
Thanks, adapted your code and it's working so far.
For i = 1 To lColumn
yes = InStr(Cells(1, i).Value, "||")
If yes <> 0 Then
Set Filldown = Range(Cells(1, i), Cells(lrc, i))
Filldown.Select
Selection.Filldown
End If
Next i

Insert row base on specific text and its occurrence

I am using a VBA code to insert rows below based on a specific text and its occurrence .
I am using the following code to do so
Sub try()
Dim c As Range
For Each c In Range("A1:A100")
If c.Value Like "*COLLECTION*" Then
c.Offset(1, 0).EntireRow.Insert
End If
Next c
End Sub
I want to have the text BALANCE below the COLLECTION cell instead of blank row.
I want to insert the BALANCE row below the last COLLECTION entry, for example if there are two collections rows serially then I want to add the BALANCE row after the 2nd collection row. but with the above VBA code I am getting blank rows below to the each collection row.
My Collection and balance rows are in the column A
Before macro Image kindly check
After macro I want like this Image kindly check
I would do this using a loop from row 1 till last filled row in column A. Then having a boolean marker which is true while the cell value in current cell is like "*COLLECTION*" but false while not. So if the current cell is not like "*COLLECTION*" but the marker is true then the last cell above the current cell was like "*COLLECTION*". Then insert a new row with "BALANCE" if that cell is not already "BALANCE".
Sub try()
Dim c As Range
Dim lRow As Long
lRow = 1
Dim lRowLast As Long
Dim bFound As Boolean
With ActiveSheet
lRowLast = .Cells(.Rows.Count, 1).End(xlUp).Row
Do
Set c = .Range("A" & lRow)
If c.Value Like "*COLLECTION*" Then
bFound = True
ElseIf bFound Then
bFound = False
If c.Value <> "BALANCE" Then
c.EntireRow.Insert
lRowLast = lRowLast + 1
c.Offset(-1, 0).Value = "BALANCE"
c.Offset(-1, 0).Font.Color = RGB(0, 0, 0)
End If
End If
lRow = lRow + 1
Loop While lRow <= lRowLast + 1
End With
End Sub
That's typically the kind of cases you want to start from the last cell, because inserting a row will mess up all counters from what is below.
In other words, the elegant for each is not really a good idea. Too unpredictable. An ugly, old simple For Step -1 is the way to go. Something like :
Sub Macro1()
For l = 100 To 1 Step -1
If Trim(Cells(l, 1)) = "COLLECTION" And Trim(Cells(l + 1, 1)) = "DEMAND" Then
Rows(CStr(l + 1) & ":" & CStr(l + 1)).Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
Cells(l + 1, 1) = "BALANCE"
End If
Next l
End Sub
Just tried on EXCEL 2013, seems to work as you want. There may be more elegant solutions, though.
EDIT : the idea is the following one :
_Begin by the last line(in fact, the last line cannot work, so one optimization could be to start from the prevo=ious one), and go to the first one
_If the line testes is "COLLECTION", and the next one is "DEMAND", then you need to insert a "BALANCE" line in between. It's done in 2 times, first insert an empty line, then add "BALANCE" in the newly created line.

VBA: Multiple matching columns sorted based on a dummy

I have a huge datasheet in excel, which i need sorted.
I need two columns to match based on a Dummy.
The dummy is either 'C'(call) or 'P'(put). The two other columns are 'expiration date' and 'strike price'.
I need to sort the datasheet so it is only containing C and P that have the same expiration date and strike price.
I've been trying go search the internet without any help. I can't really figure out how to program it in VBA.
Any help is really appreciated.
Thanks.
The solution I think: (I am addin a new column -column W- which is empty- to mark the row as "Matched" which is to be deleted after operation.)
First sub to mark the doublets (Matched Rows):
Private Sub FindDoublets()
Dim intRowC As Long
Dim intRowP As Long
Application.ScreenUpdating = False
Range("W1").EntireColumn.Insert
For intRowC = 2 To ActiveSheet.UsedRange.Rows.Count
If Cells(intRowC, 6).Value = "C" Then
For intRowP = 2 To ActiveSheet.UsedRange.Rows.Count
If Cells(intRowP, 6).Value = "P" Then
If Cells(intRowC, 4).Value = Cells(intRowP, 4).Value And Cells(intRowC, 7).Value = Cells(intRowP, 7).Value Then
Cells(intRowC, 23).Value = "Matched"
Cells(intRowP, 23).Value = "Matched"
End If
End If
Next
End If
Next
Application.ScreenUpdating = True
End Sub
Second sub to delete not matched so not marked rows:
Private Sub DeleteNotMatchedRows()
Dim intRow As Long
Application.ScreenUpdating = False
For intRow = ActiveSheet.UsedRange.Rows.Count To 2 Step -1
If Cells(intRow, 23).Value <> "Matched" Then
Rows(intRow).Delete shift:=xlShiftUp
End If
Next
Range("W1").EntireColumn.Delete
Application.ScreenUpdating = True
End Sub
In your example sheet enter this formula in column W: =IF(F2="C",TRUE,FALSE) and this one into column X: =COUNTIFS(D:D,D2,G:G,G2,W:W,NOT(W2))
Now you will have "1" in column X when actual row has a corresponding C / P row and "0" otherwise. Just need to filter for 0-s and delete the rows.
You can do the same with macro too but it's more complicated.