VBA - Remove rows from XLSX where particular text can be found within a cell - excel

I'm super new to VBA and hoping I could get some help please ... I'm looking for a succinct way to remove rows from a sheet via Macro and the VBA samples I've tried I cant get to work. I'm hoping it's something simple :)
The macro would remove a row that DOES NOT contain a specific values (from a specific column) from within an array. I sort of had it working, and full disclosure, I've borrowed code from other examples. The latest sample I'm working with just deletes everything, the other sample I was working with deleted Acro32.exe, but kept everything else. So not yet arrived at a solution.
Background: I have a CSV output from an application auditing tool, which spits out tonnes of app data from various computers. That CSV data will be copied into my master 'reporting' spreadsheet. I am only interested in reviewing and keeping data about specific applications, namely Chrome.exe, Firefox.exe, Acro32.exe and Winword.exe. The application name is always found in Column F. So, any cell contents found in Column F, that DOES NOT contain a value in an array, that entire row needs to be deleted. Ideally :) and the remaining rows will only include apps I'm interested in as defined in the array.
Any thoughts would be appreciated.
Thanks
Public Function GetLastRow(ByVal rngToCheck As Range) As Long
Dim rngLast As Range
Set rngLast = rngToCheck.Find(what:="*", searchorder:=xlByRows, searchdirection:=xlPrevious)
If rngLast Is Nothing Then
GetLastRow = rngToCheck.Row
Else
GetLastRow = rngLast.Row
End If
End Function
Sub Apps_Formatting()
Dim varList As Variant
Dim lngLastRow As Long, lngCounter As Long
Dim rngToCheck As Range, rngFound As Range
Dim rngToDelete As Range, rngDifferences As Range
Dim blnFound As Boolean
Application.ScreenUpdating = False
With ActiveSheet
lngLastRow = GetLastRow(.Cells)
'we don't want to delete our header row
Set rngToCheck = .Range("A2:A" & lngLastRow)
End With
If lngLastRow > 1 Then
With rngToCheck
'any Cell in Column F that contains one of these values are KEPT
'and if not found in cell, then the entire row is deleted.
varList = VBA.Array("Chrome.exe", "Firefox.exe", "Acro32.exe")
For lngCounter = LBound(varList) To UBound(varList)
Set rngFound = .Find( _
what:=varList(lngCounter), _
Lookat:=xlWhole, _
searchorder:=xlByRows, _
searchdirection:=xlNext, _
MatchCase:=True)
'check if we found a value we want to keep
If Not rngFound Is Nothing Then
blnFound = True
'if there are no cells with a different value then
'we will get an error
On Error Resume Next
Set rngDifferences = .ColumnDifferences(Comparison:=rngFound)
On Error GoTo 0
If Not rngDifferences Is Nothing Then
If rngToDelete Is Nothing Then
Set rngToDelete = rngDifferences
Else
Set rngToDelete = Application.Intersect(rngToDelete, rngDifferences)
End If
End If
End If
Next lngCounter
End With
If rngToDelete Is Nothing Then
If Not blnFound Then rngToCheck.EntireRow.Delete
Else
rngToDelete.EntireRow.Delete
End If
End If
Application.ScreenUpdating = True
End Sub

To follow up on this thread, should someone else benefit, the code below was provided and worked really well.
Sub a1077712b()
'https://www.mrexcel.com/forum/excel-questions/1077712-vba-remove-rows-xlsx-where-particular-text-can-found-within-cell.html
Dim i As Long, r As Range
Dim va As Variant, arr As Variant, flag As Boolean
arr = Array("Chrome.exe", "Firefox.exe", "Acro32.exe", "Winword.exe")
Set r = Range("F2", Cells(Rows.count, "F").End(xlUp))
va = r
For i = 1 To UBound(va, 1)
flag = False
For Each x In arr
If InStr(1, va(i, 1), x, 1) > 0 Then flag = True: Exit For
Next
If flag = False Then va(i, 1) = ""
Next
r = va
r.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End Sub

Related

VBA Highlight different duplicates with different colors across a table array

My question is in the title. I have searched up everywhere and this one feels like the only answer that is working:
https://stackoverflow.com/a/15180079/17038705
I have created a sample Excel file and validated that his VBA code works, the sample he shows looks like it is working too. However, when I ran it with the Excel file I am working on, I got Error 91, Object variable or With block variable not set.
After some digging, it is probably because of his Find() function that returns Nothing.
My question is why this is the case for my file and not for others. The values there are based on formulas and values of other cells, would that be a problem?
Other approaches are appreciated as well. Thanks!
Since your data contains formulas, you need to set the LookIn parameter to xlValues in the Find method. I updated the original code with these changes, take a look:
Sub Highlight_Duplicate_Entry()
Dim ws As Worksheet
Dim cell As Range
Dim myrng As Range
Dim clr As Long
Dim lastCell As Range
Set ws = ThisWorkbook.Sheets("Sheet1")
Set myrng = ws.Range("A2:D" & Range("A" & ws.Rows.Count).End(xlUp).Row)
With myrng
Set lastCell = .Cells(.Cells.Count)
End With
myrng.Interior.ColorIndex = xlNone
clr = 3
For Each cell In myrng
If Application.WorksheetFunction.CountIf(myrng, cell) > 1 Then
' addresses will match for first instance of value in range
'[================]
If myrng.Find(what:=cell, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False, after:=lastCell).Address = cell.Address Then
' set the color for this value (will be used throughout the range)
cell.Interior.ColorIndex = clr
clr = clr + 1
Else
' if not the first instance, set color to match the first instance
'[================]
cell.Interior.ColorIndex = myrng.Find(what:=cell, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False, after:=lastCell).Interior.ColorIndex
End If
End If
Next
End Sub
A slightly different approach using a Dictionary to track values vs. colors:
Sub Tester()
ColorDups Range("A1").CurrentRegion
End Sub
Sub ColorDups(rng As Range)
Dim c As Range, dict As Object, i As Long, v
Set dict = CreateObject("scripting.dictionary")
i = 0
Application.ScreenUpdating = False
For Each c In rng.Cells
v = CStr(c.Value)
If Len(v) > 0 Then
If Not dict.exists(v) Then
dict.Add v, c 'store the first cell with this value
Else
If TypeOf dict(v) Is Range Then 'second cell with this value?
i = i + 1 'next index
dict(v).Interior.ColorIndex = i 'color the first cell
dict(v) = i 'store the index
End If
c.Interior.ColorIndex = dict(v) 'color this duplicate
End If
End If
Next c
End Sub

Loop through cells and display a message if a value is not found

I have a macro that loops through cells of one sheet, looks for that value in another sheet, and then highlights the row if they match. I'd like to add a message box that would pop up if a matching value is not found. I know this is a simple problem, but I'm having trouble figuring out in which loop to put my booleans.
Sub MarkXfer_noX()
Dim rng As Range
Dim rng2 As Range
Set rng = Worksheets("Transferred Routings").UsedRange
Dim i As Integer
Dim j As Integer
Dim ProdCI As String
Dim found As Boolean
Dim intRowCount As Integer
intRowCount = Sheets("Transferred Routings").UsedRange.Rows.count
For i = 2 To intRowCount
If rng.Cells(i, 1) <> "" Then ProdCI = rng.Cells(i, 1) 'get the ProdCI number from column A if not blank
Worksheets("All_ProCI").Activate 'activate main page
Set rng2 = Worksheets("All_ProCI").UsedRange 'select a range on the main page
For j = 2 To rng2.Rows.count 'from row 2 to the end
If rng2.Cells(j, 2) = ProdCI Then 'if the ProdCI in column B matches the one we picked,
Call FillCell(j) 'call a sub in a different module and give it our current row
found = True
Else
found = False
End If
Next
Next
If found = False Then
MsgBox (ProdCI & " not found") 'Display a message if one of the items wasn't found on the main page. Currently has an error where the last one in the list always pops up.
Else
End If
End Sub
Right now it always shows a msgbox with the last value in the range no matter what.
Thanks all, here is the updated working code using the Find function
Sub MarkXfer_Find()
'Re-tooled to use the .Find function instead of looping through each
Dim rng As Range
Dim rng2 As Range
Set rng = Worksheets("Transferred Routings").UsedRange
Dim i As Integer
Dim ProdCI As String
Dim intRowCount As Integer
Dim intRowCount2 As Integer
Dim aCell As Range
intRowCount = Sheets("Transferred Routings").UsedRange.Rows.count
For i = 2 To intRowCount
If rng.Cells(i, 1) <> "" Then ProdCI = rng.Cells(i, 1) 'get the ProdCI number from column A if not blank
Worksheets("All_ProCI").Activate 'activate main page
Set rng2 = Worksheets("All_ProCI").UsedRange 'select a range on the main page
intRowCount2 = Worksheets("All_ProCI").UsedRange.Rows.count
'use the Find function to put a value in aCell
Set aCell = rng2.Range("B1:B" & intRowCount2).Find(What:=ProdCI, LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
If Not aCell Is Nothing Then
'MsgBox ProdCI & " found"
Call FillCell(aCell.row)
Else 'If aCell is blank display msgbox
MsgBox "ProdCI """ & ProdCI & """ not found"
End If
Next
End Sub

Selecting Range between two words then based on a different column deleting them

I made an original post (Selecting a Range depended on two Key Words). My code was correct however it doesn't do what I needed it to do. I need help/ guidance to manipulate the code so that between Revenue and total revenue we look at column J if it is empty the entire row is deleted. I tried my best but as I am currently learning VBA I am struggling to find even how to approach it.
Code thus far:
Dim rngFirst As Range
Dim rngLast As Range
Dim rngUnion As Range
Application.ScreenUpdating = False
With Sheets("Input")
'Find the start and stop
Set rngFirst = .Cells.Find(what:="Performance Income", lookat:=xlWhole, _
LookIn:=xlValues, MatchCase:=False)
Set rngLast = .Cells.Find(what:="Miscellaneous Income", _
lookat:=xlWhole, LookIn:=xlValues, MatchCase:=False)
Set rngUnion = Range(rngFirst.Address, rngLast.Address)
rngUnion.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End With
Application.ScreenUpdating = True
I appreciate all the help thus far and any help given. Thank you.
I recommend to use Match to find the rows where "Revenue" and "Total Revenue" are. Then check between these rows if there are blanks in column J .SpecialCells(xlCellTypeBlanks) and delete the EntireRow.
Option Explicit
Public Sub DeleteEmpty()
Dim wsInput As Worksheet 'define worksheet
Set wsInput = ThisWorkbook.Worksheets("Input")
Dim FirstRow As Long, LastRow As Long
On Error Resume Next 'Next line throws error if "Revenue" or "Total Revenue" is not found
FirstRow = Application.WorksheetFunction.Match("Revenue", wsInput.Range("A:A"), False) + 1
LastRow = Application.WorksheetFunction.Match("Total Revenue", wsInput.Range("A:A"), False) - 1
On Error GoTo 0 'Always re-activate error handling!
'Check if both "Revenue" and "Total Revenue" were found
If FirstRow = 0 Or LastRow = 0 Then
MsgBox "Revenue or Total Revenue not found"
Exit Sub
End If
'Find empty cells in column J between FirstRow (Revenue) and LastRow (Total Revenue)
Dim EmptyCellsInJ As Range
On Error Resume Next
Set EmptyCellsInJ = wsInput.Range(wsInput.Cells(FirstRow, "J"), wsInput.Cells(LastRow, "J")).SpecialCells(xlCellTypeBlanks)
On Error GoTo 0
'If there are empty cells delete their rows
If Not EmptyCellsInJ Is Nothing Then
EmptyCellsInJ.EntireRow.Delete
Else
MsgBox "nothing to delete"
End If
End Sub

My VBA method is causing Excel to crash - I cannot see the mistake

EDIT: I may have spotted an issue as soon as posting it the myRange
variables dont seem to be doing anything - so I'm feeling they were
there from a method i was using ages ago and there decided to crop out
I'll remove the whole myRange variable and see what happens
Set myRange = ActiveSheet.Range("1:1")
Set myRange = ActiveSheet.Range("A:A")
EDIT 2: Ok so changing the numCols and numRows functions to only use
numCols = ActiveSheet.Cells(1, Columns.Count).End(xlToLeft).Column
numRows = ActiveSheet.Cells(Rows.Count, 1).End(xlUp).row
They now return the correct row and Column numbers
But now when I run selectBlock() it gives me runtime error 28 "Out of Stack Space"
Hello All, I've been writing code to be able to go through multiple sheets and copy the data across to a master workbook
Im coding this to work on any file depending what you pass to it - which has been fine
What im having problems with is the Functions I have made which find the last populated row for any sheet I pass to it
Sub test()
selectBlock().Select
End Sub
Function selectBlock() As Range
Dim row As Integer: row = numRows() 'Finds last populated row
Dim col As Integer: col = numCols() 'Finds last populated column
Set selectBlock() = Range("A2:" & Cells(row, col).Address)
'sets this area starting from cell A2 as the Range
End Function
Function numCols() As Integer
Dim myRange As Range
Set myRange = ActiveSheet.Range("1:1") 'Checks first row to see how many populated columns there are
numCols = ActiveSheet.Cells(1, Columns.Count).End(xlToLeft).Column
End Function
Function numRows() As Integer
Dim myRange As Range
Set myRange = ActiveSheet.Range("A:A") 'Checks first columns to see how many populated rows there are
numRows = Range("A" & Rows.Count).End(xlUp).row
End Function
When I call the test Sub it causes Excel to hang then crash with no error code
So i imagine im creating some kind of loop or critical error that isnt handled by excel very well
Any help with this would be really appreciated
I can also understand if how im going about it is incredibly stupid
I used to code in Java and maybe im using techniques or pitfalls that I never got rid of - Im self taught at VBA like most and so never learnt official coding practices for VBA
Lot of things here
Fully qualify your cells
Use Long and not Integer when working with row and columns
Use error handling. This will avoid the Excel crashing.
Try this
Sub test()
On Error GoTo Whoa
selectBlock().Select
Exit Sub
Whoa:
MsgBox Err.Description
End Sub
Function selectBlock() As Range
Dim row As Long: row = numRows() 'Finds last populated row
Dim col As Long: col = numCols() 'Finds last populated column
Set selectBlock = ActiveSheet.Range("A2:" & ActiveSheet.Cells(row, col).Address)
End Function
Function numCols() As Long
numCols = ActiveSheet.Cells(1, ActiveSheet.Columns.Count).End(xlToLeft).Column
End Function
Function numRows() As Long
numRows = ActiveSheet.Range("A" & ActiveSheet.Rows.Count).End(xlUp).row
End Function
Replace
Set selectBlock() = Range("A2:" & Cells(row, col).Address)
to
Set selectBlock = Range("A2:" & Cells(row, col).Address)
it looks recursive :P
There are safer ways to find the LastRow and LastCol, I like the Find function.
See more detailed in my code's comments.
Code
Sub test()
Dim Rng As Range
Set Rng = selectBlock
Rng.Select '<-- Not sure why you need to Select ?
End Sub
'============================================================
Function selectBlock() As Range
Dim LastRow As Long
Dim LastCol As Long
LastRow = FindLastRow(ActiveSheet) 'Finds last populated row
LastCol = FindLastCol(ActiveSheet) 'Finds last populated column
Set selectBlock = Range(Cells(2, "A"), Cells(LastRow, LastCol))
End Function
'============================================================
Function FindLastCol(Sht As Worksheet) As Long
' This Function finds the last col in a worksheet, and returns the column number
Dim LastCell As Range
With Sht
Set LastCell = .Cells.Find(What:="*", After:=.Cells(1), Lookat:=xlPart, LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False)
If Not LastCell Is Nothing Then
FindLastCol = LastCell.Column
Else
MsgBox "Error! worksheet is empty", vbCritical
End
End If
End With
End Function
'============================================================
Function FindLastRow(Sht As Worksheet) As Long
' This Function finds the last row in a worksheet, and returns the row number
Dim LastCell As Range
With Sht
Set LastCell = .Cells.Find(What:="*", After:=.Cells(1), Lookat:=xlPart, LookIn:=xlFormulas, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False)
If Not LastCell Is Nothing Then
FindLastRow = LastCell.row
Else
MsgBox "Error! worksheet is empty", vbCritical
End
End If
End With
End Function

Inserting a blank row after a string in Excel

I am trying to create a macro in excel 2010 that finds every cell in a sheet with a value of "All Customers." Every time that value is found I need a blank row inserted below it. Thought it would be pretty simple but I have searched I many forums and tried to use some sample code and I can't get it to work properly. I am a complete newb when it comes to VBA stuff. Thought I would post here and go do some light reading on basics of VBA.
If anyone has any good training resources, please post those as well.
Thanks in advance!
EDIT: In my OP, I neglected to mention that any row that contains a value of "All Customers" would ideally be highlighted and put in bold, increased size font.
These actions are something that an old Crystal Report viewing/formatting program used to handle automatically when pulling the report. After we upgraded the program I learned that this type of formatting ability had been removed with the release of the newer version of the program, according to the software manufacturer's tech support. Had this been defined in the release notes I would have not performed the upgrade. Regardless, that is how I found myself in this macro disaster.
Something like this code adpated from an article of mine here is efficient and avoids looping
It bolds and increase the font size where the text is found (in the entire row, as Tim points out you should specify whether you meant by cell only)
It adds a blank row below the matches
code
Option Explicit
Const strText As String = "All Customers"
Sub ColSearch_DelRows()
Dim rng1 As Range
Dim rng2 As Range
Dim rng3 As Range
Dim cel1 As Range
Dim cel2 As Range
Dim strFirstAddress As String
Dim lAppCalc As Long
Dim bParseString As Boolean
'Get working range from user
On Error Resume Next
Set rng1 = Application.InputBox("Please select range to search for " & strText, "User range selection", ActiveSheet.UsedRange.Address(0, 0), , , , , 8)
On Error GoTo 0
If rng1 Is Nothing Then Exit Sub
'Further processing of matches
bParseString = True
With Application
lAppCalc = .Calculation
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
'a) match string to entire cell, case insensitive
'Set cel1 = rng1.Find(strText, , xlValues, xlWhole, xlByRows, , False)
'b) match string to entire cell, case sensitive
'Set cel1 = rng1.Find(strText, , xlValues, xlWhole, xlByRows, , True)
'c)match string to part of cell, case insensititive
Set cel1 = rng1.Find(strText, , xlValues, xlPart, xlByRows, , False)
'd)match string to part of cell, case sensititive
' Set cel1 = rng1.Find(strText, , xlValues, xlPart, xlByRows, , True)
'A range variable - rng2 - is used to store the range of cells that contain the string being searched for
If Not cel1 Is Nothing Then
Set rng2 = cel1
strFirstAddress = cel1.Address
Do
Set cel1 = rng1.FindNext(cel1)
Set rng2 = Union(rng2.EntireRow, cel1)
Loop While strFirstAddress <> cel1.Address
End If
'Further processing of found range if required
If bParseString Then
If Not rng2 Is Nothing Then
With rng2
.Font.Bold = True
.Font.Size = 20
.Offset(1, 0).EntireRow.Insert
End With
End If
End If
With Application
.ScreenUpdating = True
.Calculation = lAppCalc
End With
End Sub
Public Sub InsertRowAfterCellFound()
Dim foundRange As Range
Set foundRange = Cells.Find(What:="yourStringOrVariant", After:=ActiveCell) 'Find the range with the occurance of the required variant
Rows(foundRange.Row + 1 & ":" & foundRange.Row + 1).Insert 'Insert a new row below the row of the foundRange row
foundRange.Activate 'Set the found range to be the ActiveCell, this is a quick and easy way of ensuring you aren't repeating find from the top
End Sub
You may need to add error handling to the code as you will get an error if no cell with the specified value is found.
Assuming this is on the first sheet ("sheet 1"), here is a slow answer:
Sub InsertRowsBelowAllCustomers()
'Set your worksheet to a variable
Dim sheetOne as Worksheet
Set sheetOne = Worksheets("Sheet1")
'Find the total number of used rows and columns in the sheet (where "All Customers" could be)
Dim totalRows, totalCols as Integer
totalRows = sheetOne.UsedRange.Rows.Count
totalCols = sheetOne.UsedRange.Columns.Count
'Loop through all used rows/columns and find your desired "All Customers"
Dim row, col as Integer
For row = 1 to totalRows
For col = 1 to totalCols
If sheetOne.Cells(row,col).Value = "All Customers" Then
Range(sheetOne.Cells(row,col)).Select
ActiveCell.Offset(1).EntireRow.Insert
totalRows = totalRows + 1 'increment totalRows because you added a new row
Exit For
End If
Next col
Next row
End Sub
This function starts from the last row and goes back up to the first row, inserting an empty row after each cell containing "All Customers" on column A:
Sub InsertRowsBelowAllCustomers()
Dim R As Integer
For R = UsedRange.Rows.Count To 1 Step -1
If Cells(R, 1) = "All Customers" Then Rows(R + 1).Insert
Next R
End Sub
The error is because the worksheet was not specified in used range.
I have slightly altered the code with my text being in column AJ and inserting a row above the cell.
Dim R As Integer
For R = ActiveSheet.UsedRange.Rows.Count To 1 Step -1
If Range("AJ" & R) = "Combo" Then Rows(R).Insert
Next R

Resources