How to select cells NOT containing a certain word - vba

I have an Excel table, in which some of the cells of the C column contain the word "Rinse" (the other cells have various other contents).
Using VBA code, here's how I would select all rows containing the word "Rinse" in the C column - this code works fine.
For i = 3 To 300
If Cells(i, 3).Value = "Rinse" Then
Rows(i).Select
Selection.FormatConditions.Delete
End If
Next
However, I want to do exactly the opposite, namely to select all rows NOT containing the word "Rinse" in the C column. I've tried the following, but it doesn't work.
For i = 3 To 300
If Cells(i, 3).Value = Not "Rinse" Then
Rows(i).Select
Selection.FormatConditions.Delete
End If
Next
How do I get this done?

Use the Instr function, like this:
If Instr(Cells(i, 3).Value, "Rinse") = 0 Then

Change this line of your code ( <> not equals to)
If Cells(i, 3).Value <> "Rinse" Then

The Like operator can be useful here:
If Not Cells(i, 3).Value Like "*Rinse*" Then
If "Rinse" can be found anywhere in your cell value

You could filter out the Rinse values and then select the visible cells.
Could be quicker than looking at each individual cell.
Public Sub Test()
Dim lRow As Long
With ThisWorkbook.Worksheets("Sheet1")
lRow = .Cells(.Rows.Count, 3).End(xlUp).Row
With .Range(.Cells(1, 3), .Cells(lRow, 3))
.AutoFilter Field:=1, Criteria1:="<>*Rinse*"
'Can replace Select in next row with .FormatConditions.Delete
.SpecialCells(xlCellTypeVisible).Select
End With
.ShowAllData
End With
End Sub

The advantage of this code is in its speed. Acceleration is achieved by referencing the sheet only once for every row and only once for the result, and by formatting only the used range columns instead of the entire rows.
Private Sub SelectNonContiguousRange()
Dim RngAddress() As String
Dim i As Long
Dim R As Long
ReDim RngAddress(300) ' this number should be
With ActiveSheet
For R = 3 To 300 ' equal to this number
' use = (equal) or <> (unequal) as required:
If .Cells(R, "C").Value <> "Rinse" Then
' If .Cells(R, "C").Value = "Rinse" Then
RngAddress(i) = .Range(.Cells(R, "A"), _
.Cells(R, .UsedRange.Columns.Count)).Address
i = i + 1
End If
Next R
ReDim Preserve RngAddress(i - 1)
.Range(Join(RngAddress, ",")).FormatConditions.Delete
End With
End Sub
BTW, you can use a variation of this code to select multiple rows simultaneously (like you can do with Ctl+Click), for example all rows containing the word "Rinse".

#Renee - Change the if condition line as shown below.
For i = 3 To 300
If Cells(i, 3).Value <> "Rinse" Then
Rows(i).Select
Selection.FormatConditions.Delete
End If
Next

Related

How do I find the last row in a column I'm currently looping through

c specifies the columns I'm looping through and the Style is a highlight applied to blank cells. If the cell is blank I need the ID of that row (Cells(i,4)) to be copied to a reports page in the column c that I'm currently looping through. For readability I'm trying to copy each instance in the next available cell of that row but as you can imagine I'm getting an error at the Range(c & Rows.Count) portion of the code.
I'm aware that I can put A or any other column letter there but i'm just wondering if i were to be able to put the variable that I'm iterating with there instead. Any tips for this?
For c = 1 To 103
For i = 1 To coor(2)
If Cells(i, c).Style = "60% - Accent2" Then
Cells(i, 4).Copy Sheets("ReportsPage").Range(c & Rows.Count).End(xlUp).Offset(1, 0)
End If
Next i
Next c
Use Cells() instead of Range(). Cells() allows for the use of cardinal location:
For c = 1 To 103
For i = 1 To coor(2)
If Cells(i, c).Style = "60% - Accent2" Then
Cells(i, 4).Copy Sheets("ReportsPage").Cells(Rows.Count,c).End(xlUp).Offset(1, 0)
End If
Next i
Next c
One more note, one should always append any range object with their parent sheet, even if it is the activesheet:
With ActiveSheet
For c = 1 To 103
For i = 1 To coor(2)
If .Cells(i, c).Style = "60% - Accent2" Then
.Cells(i, 4).Copy Sheets("ReportsPage").Cells(Rows.Count,c).End(xlUp).Offset(1, 0)
End If
Next i
Next c
End With
You can use this to find the last column:
Dim LastCol As Integer
With ActiveSheet
LastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
End With

VBA code for finding a partial string match

I'm looking to use this code but modified so that it finds any cells that contain the words Abuse Neglect (like a partial match) - and then subsequently deletes the row. Any help is appreciated!
Sub TakeOutAllOtherCourses()
Last = Cells(Rows.Count, "D").End(xlUp).Row
For i = Last To 1 Step -1
If (Cells(i, "D").Value) = "Abuse Neglect" Then
'Cells(i, "A").EntireRow.ClearContents ' USE THIS TO CLEAR CONTENTS BUT NOT DELETE ROW
Cells(i, "A").EntireRow.Delete
End If
Next i
Try Instr
Sub TakeOutAllOtherCourses()
last = Cells(Rows.Count, "D").End(xlUp).Row
For i = last To 1 Step -1
If InStr(Cells(i, "D").Value, "Abuse Neglect") > 0 Then
Cells(i, "A").EntireRow.Delete
End If
Next i
End Sub
Check how Like operator works in this link (Like Operator - Visual Basic)
Code:
Sub TakeOutAllOtherCourses()
Last = Cells(Rows.Count, "D").End(xlUp).Row
For i = Last To 1 Step -1
If (Cells(i, "D").Value) Like "Abuse Neglect*" Then
'Cells(i, "A").EntireRow.ClearContents ' USE THIS TO CLEAR CONTENTS BUT NOT DELETE ROW
Cells(i, "A").EntireRow.Delete
End If
Next i
End Sub
Should your column D have no blanks, here’s a possibile (but untested) way with neither AutoFilter nor loops:
Sub TakeOutAllOtherCourses()
With Range("D1", Cells(Rows.Count, "D").End(xlUp))
.Replace What:="Abuse Neglect*", Replacement:="", LookAt:=xlPart
.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End With
End Sub

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

VBA to past certain cell values on different worksheet in predetermined columns

Gods of VBA,
I would like to request your help on some code i can't seem to get working straight.
Purpose,
When a row has a cell Value "x" on row A in sheet 'Dump', i would like to past certain values in Sheet 'test'.
The values that need to be posted on Sheet 'test', are in column B, D, F and L.
Value from column B, Sheet 'Dump' should go to D4, in sheet 'test'.
Value from column D, Sheet 'Dump' should go to C4, in Sheet 'test'.
Value from column F, Sheet 'Dump' should go to A4, in Sheet 'test'.
Value from column L, Sheet 'Dump' should go to E4, in Sheet 'test'.
Ofcourse i'm trying to make the VBA loop as that when multiple rows on Sheet 'Dump' contains the character 'x', it continues from D/C/A/E4 to the next row.
The code I already have working is posted here:
Sub test()
Dim i, LastRow
LastRow = Sheets("Dump").Range("A" & Rows.Count).End(xlUp).Row
Sheets("test").Range("A2:K200").ClearContents
For i = 2 To LastRow
If Sheets("Dump").Cells(i, "A").Value = "x" Then
Sheets("Dump").Range(Cells(i, "B"), Cells(i, "B")).Copy
Destination:=Sheets("test").Range("A" & Rows.Count).End(xlUp).Offset(1)
End If
Next i
End Sub
Have been trying with a lot of different sources of VBA, and some tweaking to it. If i started with a wrong source, or am making some n00b-mistakes, please direct me to what i did wrong. Just trying to learn, while coding.
Tim posted the better way to copy values only but here is what is the problem with your code:
The syntax for copying is
sourceRange.Copy Destination:=destinationRange
The := specifies an option/paramter to the .Copy method. It can be confusing because there are no parentheses around the arguments like you could expect from other languages.
someMethod(argument1, argument2)
would be
someMethod argument1, argument2
if there is nothing else in the line (otherwise you need parentheses).
You can specify what argument you use by naming it and using :=. This is especially useful for optional arguments or to keep your code readable (you might not remember what each argument is in a few months). Some people keep parameters empty but I think it's obvious why something like
someMethod paramName1:=True, paramName4:=False, paramName5:=True
is easier to read than
someMethod True, , , False, True
(I am assuming the parameter names are descriptive like Destination).
The parameters of a function need to be in the same row as the function. To concatenate the rows, remove the linebreak (duh) or place an _ at the end of the line (if it get's to long).
Example with parentheses and linebreaks:
Set someRange = rangeToSearch.Find( _
What:="abc", _
LookIn:=xlValues, _
MatchCase:=True)
Example without parenthesis and linebreaks:
destinationRange.PasteSpecial Paste:=xlPasteValues, skipblanks:=True
You could try the following.
Sub test()
Dim i, LastRow
LastRow = Sheets("Dump").Range("A" & Rows.Count).End(xlUp).Row
Sheets("test").Range("A2:K200").ClearContents
j = 4
For i = 2 To LastRow
If Sheets("Dump").Cells(i, "A").Value = "x" Then
Sheets("test").Cells(j, 4) = Sheets("Dump").Cells(i, 2).Value
Sheets("test").Cells(j, 3) = Sheets("Dump").Cells(i, 3).Value
Sheets("test").Cells(j, 1) = Sheets("Dump").Cells(i, 6).Value
Sheets("test").Cells(j, 5) = Sheets("Dump").Cells(i, 12).Value
j = j + 1
End If
Next i
End Sub
You need a separate way of tracking each row in the test sheet, hence adding j = 4 (because you want to start on row 4).
EDIT
I would define your sheets if you call them a a lot.
Sub test()
Dim i, LastRow, source as Worksheet, dest as Worksheet
Set source = ActiveWorkbook.Sheets("Dump")
Set dest = ActiveWorkbook.Sheets("test")
LastRow = source.Range("A" & Rows.Count).End(xlUp).Row
dest.Range("A2:K200").ClearContents
j = 4
For i = 2 To LastRow
With source
If .Cells(i, "A").Value = "x" Then
dest.Cells(j, 4) = .Cells(i, 2).Value
dest.Cells(j, 3) = .Cells(i, 3).Value
dest.Cells(j, 1) = .Cells(i, 6).Value
dest.Cells(j, 5) = .Cells(i, 12).Value
j = j + 1
End If
End With
Next i
End Sub

VBA Code for Conditional Loop

I am trying to create a conditional loop macro in Excel. Column B contains a last name, Column C contains a first name, and Column D contains a first and last name. I am trying to get the macro to detect when Column D = Column C + Column B.
If D = C + B, then clear contents of D.
So, the following works for a single row:
Sub ClearContentsD ()
If Range("D1").Value = Range("C1").Value + Space(1) + Range("B1") Then Range("D1").ClearContents
End Sub
It does not work without the added Space(1), and I cannot get it to loop through the whole worksheet:
Sub ClearContentsLoop()
Application.ScreenUpdating = False
Dim i As Long
For i = 1 To Rows.Count
Next i
Do While Cells(i, 4).Value = Cells(i, 3).Value + Space(1) + Cells(i, 2).Value
Cells(i, 4).ClearContents
Loop
Application.ScreenUpdating = True
End Sub
VBA doesn't like my Do While. Any help would be greatly appreciated.
CJ
Some issues:
You must concatenate strings with &. The plus (+) is for addition;
Your For loop is not doing anything: its body is empty;
Your Do While Loop will at most run once, because i is not incremented;
It is a mystery why you would want two loops (For and Do While);
A sheet has many rows of which you only use a fraction, so don't loop through all of them (For) and use UsedRange.
Possible correction:
Sub ClearContentsLoop()
Dim i As Long
Application.ScreenUpdating = False
For i = 1 To ActiveSheet.UsedRange.Rows.Count
If Cells(i, 4).Value = Cells(i, 3).Value & " " & Cells(i, 2).Value Then
Cells(i, 4).ClearContents
End If
Next i
Application.ScreenUpdating = True
End Sub
There is a way to ignore the space in the values you are evaluating. Try this:
Application.ScreenUpdating = False
Dim i As Long
For i = 1 To Rows.Count
If InStr(1, Cells(i, 4).Value, Cells(i, 2).Value, vbTextCompare) > 0 And InStr(1, Cells(i, 4).Value, Cells(i, 3).Value, vbTextCompare) > 0 Then Cells(i, 4).ClearContents
Next i
Application.ScreenUpdating = True
Explanation:
By using the InStr function, you are testing for the presence of one text string inside of another, and if at least one match is found, then the function returns a non-zero value (the position where the match was found). In the above example, you are testing for the presence of the first name and last name at the same time, and if both are found, then the code clears out the contents of the cell.
And, as was pointed out in the comments section, you need to do this inside the loop so that all cells down the length of the worksheet are evaluated and updated as specified.
Be sure to test this on a COPY of your original data so that you don't lose the original values in case you want to roll back your changes! ;)