VBA - running an excel macro take forever - excel

I am running this macro and its either taking forever or something is wrong with the way I wrote it. I am new to Excel Macros, and I can't seem to figure out how to write this better that it won't crash my PC.
Sub Test()
ScreenUpdating = True
Dim maxRows As Integer
maxRows = 2332
For i = maxRows To 2 Step -1
'if the cell is empty, delete the row
'otherwise delete the first cell of the row and shift over
If Cells(i, 2).Value = "" Then
Rows(i).Delete
Else
Cells(i, 1).Delete
End If
Next i
ScreenUpdating = False
End Sub

In general, whenever delete is used, it is a good idea to do it only once, for all the cases. However, in the case of the OP, there are two cases separately - deleting a whole row and deleting only one cell. Thus, the delete method could be used twice:
Sub TestMe()
Dim maxRows As Long: maxRows = 2332
Dim myRangeRows As Range
Dim myRangeCells As Range
Dim i As Long
For i = maxRows To 2 Step -1
With Worksheets(1)
If .Cells(i, 2) = "" Then
If myRangeRows Is Nothing Then
Set myRangeRows = .Rows(i)
Else
Set myRangeRows = Union(.Rows(i), myRangeRows)
End If
Else
If myRangeCells Is Nothing Then
Set myRangeCells = .Cells(i, 1)
Else
Set myRangeCells = Union(.Cells(i, 1), myRangeCells)
End If
End If
End With
Next i
If Not myRangeRows Is Nothing Then myRangeRows.Delete
If Not myRangeCells Is Nothing Then myRangeCells.Delete
End Sub
Additionally, whenever the cell in Excel is referred like this Cells(i, 2).Value, then the referring is either to the ActiveSheet or to the Worksheet in which the code resides. Thus, it is a good practice to explicitly mention the worksheet, like in this case:
With Worksheets(1)
If .Cells(i, 2) = "" Then

Related

VBA Index Match with a loop with two conditions

I hope that someone could help me with an index match formula that is made using a loop and storing the results data on the column.
Let's say that my data is following to make it simple:
We have an employee column and a salary column. I want to find all the salary options for HR employees.
I would like to store automatically all the results found on the column J (Researched input is in column I). And I want to finish the loop after not finding any new values.
Here is the data:
My initial code is down below without a loop to go down on the range:
Sub test()
Dim oCell As Range
Dim i As Long
i = 1
Do While Worksheets("Sheet1").Cells(i, 9).Value <> ""
Set oCell = Worksheets("Sheet1").Range("A:A").Find(What:=Worksheets("Sheet1").Cells(i, 9))
If Not oCell Is Nothing Then Worksheets("Sheet1").Cells(i, 10) = oCell.Offset(0, 1)
i = i + 1
Loop
End Sub
The problem stems from two main things:
The .Find range you are searching is the entire column A, which is then set to a .Range object (oCell). However, from my VBA understanding the .Find method cannot apply the cell address of each instance of the string/search parameter you are looking for. It will only apply the cell address of the first one it finds. To set a .Range object of non-contiguous rows you could use UNION function.
The .Find(What:= ... is set to a dynamic range which moves down column I as the loop continues. This means it will never find a match because it is searching the preceding column.
Here is a suggested solution, which hopefully you can adapt to your real world data:
Option Explicit
'
Sub test()
Dim oCell As Range
Dim i As Long
i = 1
Do While Worksheets("Sheet1").Cells(i, 2).Value <> ""
' Included as a sense check when stepping through your code to confirm loop is on correct cell
'Debug.Print Cells(i, 2).Address
'Debug.Print Cells(i, 2).Value
'Debug.Print "NEXT"
Set oCell = Worksheets("Sheet1").Range("A1:A10").Find(What:="HR")
If Not oCell Is Nothing Then Worksheets("Sheet1").Cells(i, 3) = oCell.Offset(0, 1)
i = i + 1
Loop
End Sub
Try this:
Option Explicit
Sub test()
Dim i As Long
Dim wb as Excel.Workbook
Dim ws as Excel.Worksheet
i = 2 ' we don't need the header
set wb = ActiveWorkBook
set ws = wb.Sheets("Sheet1") ' or wb.Sheets(1)
Do While ws.Cells(i, 1) <> ""
If ws.Cells(i,1) = "HR" then
ws.Cells(i, 3) = ws.Cells(i,2)
End If
i = i + 1
Loop
End Sub
Tested and found working

Type mismatch error with a if then statement

I am trying to write a If then statement that cant take out the rows on my worksheet that have been declined or void. So I am trying to get it to look into a Column then delete the rows with those values.
If Columns("K:K") = "Declined" Or "Void" Then
Selection.Delete Shift:=x1Up
End If
You'll instead need to loop through your range. One way to do it is:
Sub t()
Dim rng As Range, curCel As Range
Dim i As Long
Set rng = Range("K1:K100") ' Change as needed
For i = rng.Cells(rng.Rows.Count, 1).Row To rng.Cells(1, 1).Row Step -1
Set curCel = Cells(i, rng.column)
If curCel.Value = "Declined" or curCel.value = "Void" Then
curCel.EntireRow.Delete
End If
Next i
End Sub
you can loop through your cells and remove the rows. I would fully qualify your sheet and try to avoid using ActiveSheet. In the example below we are going from the bottom to the top so not to revisit cells twice or adjust the increment counter.
Dim i As Long
Dim iUsedRange As Long
iUsedRange = ActiveSheet.UsedRange.Rows.Count
For i = iUsedRange To 1 Step -1
If ActiveSheet.Cells(i, 11).Value = "Declined" Or ActiveSheet.Cells(i, 11).Value = "Void" Then
ActiveSheet.Cells(i, 11).EntireRow.Delete Shift:=xlUp
End If
Next i

Check if cell is text and if so, delete row

UPDATE:
Data set is made of strings that are number though (I don't get it) -> I can do all the math stuff with them as with regular numbers.
Problem is I need to separate cells that look like this "186.85" and cells that look like this "1.76 Dividend".
====================
I need a loop to check row by row if the cell contains some text (word "dividend" specifically) or if it's just number. If it is a text, then delete it and move to the next row.
It does some deleting BUT it wipes like 50 rows of data almost every time (I have only two text populated rows in this particular data set). These rows are numbers.
Dim i As Long
i = 2
Do
If WorksheetFunction.IsText(Sheets("Data").Cells(i, 5)) = True Then
If Not Worksheets("Data").Cells(i, 5).Value = "" Then
Worksheets("Data").Rows(i).Delete
End If
End If
i = i + 1
Loop Until i = 100
I expected to loop through the data and delete the entire row if a cell contains text.
This code so far deletes things kinda randomly.
The below has been updated to a dynamic range. This will not need modification regardless of how many rows your sheet has.
More importantly, deleting rows inside a loop will cause your range to shift at every deletion. The way around this is to loop backwards
OR, even better..
Don't delete cells inside your loop. Every time your criteria is met, you force an action (deletion). Instead, gather up all of the cells to be deleted inside your loop and then delete the entire collection (Union) all at once outside of the loop. This requires 1 action in total rather 1 action per text instance
Sub Looper()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1") '<-- Update sheet name
Dim i As Long, LR As Long
Dim DeleteMe As Range
LR = ws.Range("E" & ws.Rows.Count).End(xlUp).Row
For i = 2 To LR
If WorksheetFunction.IsText((ws.Range("E" & i))) Then
If Not DeleteMe Is Nothing Then
Set DeleteMe = Union(DeleteMe, ws.Range("E" & i))
Else
Set DeleteMe = ws.Range("E" & i)
End If
End If
Next i
If Not DeleteMe Is Nothing Then DeleteMe.EntireRow.Delete
End Sub
Try something like this:
Sub test()
Dim i As Long
For i = 100 To 2 Step -1
With ThisWorkbook.Worksheets("Data")
If WorksheetFunction.IsText(.Cells(i, 5)) = True Then
If Not .Cells(i, 5).Value = "" Then
.Rows(i).EntireRow.Delete
End If
End If
End With
Next i
End Sub
You can use SpecialCells with xlCellTypeConstants-2... No need for a loop. See Range.SpecialCells method (Excel) and XlSpecialCellsValue enumeration (Excel)
Is this what you are trying?
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim rng As Range
'~~> Change this to the relevant sheet
Set ws = Sheet1
With ws
On Error Resume Next
Set rng = .Columns(5).SpecialCells(xlCellTypeConstants, 2)
On Error GoTo 0
If Not rng Is Nothing Then rng.EntireRow.Delete
End With
End Sub
Thank you all for quick responses and effort.
I managed to get this solution working with InStr() function:
''loop through the Data and remove all rows containing text (Dividend payments which we don't need)
Dim i As Long
Dim ws As Worksheet
Dim searchText As String
Dim searchString As String
i = 2
Set ws = Sheets("Data")
Do Until ws.Cells(i, 2).Value = ""
searchText = "Dividend"
searchString = ws.Cells(i, 2).Value
If InStr(searchString, searchText) Then
ws.Rows(i).Delete
End If
i = i + 1
Loop

IsEmpty functionality issue

can anyone please advise me on what I am doing wrong with this procedure? It should delete about 6 rows in my file, but when I run it, no effect. In about 6 rows, there is no data in columns B and C, their position is dynamic and I want to get rid of those rows.
Thank you
Dim lastrow As Integer
lastrow = Application.WorksheetFunction.CountA(ThisWorkbook.Sheets("ISSUES").Range("D1:D5000"))
For i = 1 To lastrow
If IsEmpty(ThisWorkbook.Sheets("ISSUES").Cells(i, 2)) = True And IsEmpty(ThisWorkbook.Sheets("ISSUES").Cells(i, 3)) = True Then
ThisWorkbook.Sheets("ISSUES").Cells(i, 2).EntireRow.Delete
End If
Next i
The second issue will be performance. Deleting one row at a time will make your macro very slow for large set of rows, like you are looking for 5000 rows here.
Best way is to club them together and then delete in one go. Also helps to avoid reverse loop.
Sub test()
Dim lastrow As Long
Dim rngDelete As Range
lastrow = Application.WorksheetFunction.CountA(ThisWorkbook.Sheets("ISSUES").Range("D1:D5000"))
For i = 1 To lastrow
If IsEmpty(ThisWorkbook.Sheets("ISSUES").Cells(i, 2)) = True And IsEmpty(ThisWorkbook.Sheets("ISSUES").Cells(i, 3)) = True Then
'ThisWorkbook.Sheets("ISSUES").Cells(i, 2).EntireRow.Delete
'/Instead of deleting one row at a time, club them in a range. Also no need for reverse loop
If rngDelete Is Nothing Then
Set rngDelete = ThisWorkbook.Sheets("ISSUES").Cells(i, 2)
Else
Set rngDelete = Union(rngDelete, ThisWorkbook.Sheets("ISSUES").Cells(i, 2))
End If
End If
Next i
'/ Now delete them in one go.
If Not rngDelete Is Nothing Then
rngDelete.EntireRow.Delete
End If
End Sub
Another solution:
Dim lastrow As Integer
Dim cond1, cond2 As Range
lastrow = Application.WorksheetFunction.CountA(ThisWorkbook.Sheets("ISSUES").Range("D1:D5000"))
For i = lastrow To 1 Step -1
Set cond1 = ThisWorkbook.Sheets("ISSUES").Cells(i, 2)
Set cond2 = ThisWorkbook.Sheets("ISSUES").Cells(i, 3)
If cond1.Value = "" Then
cond1.ClearContents
End If
If cond2.Value = "" Then
cond2.ClearContents
End If
If IsEmpty(cond1.Value) = True And IsEmpty(cond2.Value) = True Then
ThisWorkbook.Sheets("ISSUES").Cells(i, 2).EntireRow.Delete
End If
Next i

Convert a range's value from text to Number

I want to delete negative values in a range in excel. I have a code that deletes but not all the values.
I got to know that I should first change the value to numeric type. I have the below code in which I have tried to do so with cDec and Convert.ToInt32 but not successful. I am new to vba, I don't know much about its data types. Your help will be highly appreciable:
Sub Button1_Click()
Dim ws As Worksheet
Dim i As Integer
i = 1
Set ws = Sheets("Recovered_Sheet1")
ws.Activate
Dim r As Excel.Range
For Each r In Range("A1:A250").Rows
If Not IsEmpty(ActiveCell.Value) Then
'move to the row below
ActiveCell.Offset(1, 0).Select
ActiveCell.Value = CDec(ActiveCell.Value)
End If
Next r
Do While Cells(i, 1).Value <> ""
If Cells(i, 1) < 0 Then
Cells(i, 1).EntireRow.Delete
End If
i = i + 1
Loop
End Sub
Here is one way of doing that. Note that when deleting rows, you should work from the bottom up. Also, you don't need to change the Excel data type before running this macro, unless you have some other reason to do so (in which case there are more efficient methods than going cell by cell).
Edit Since text and blanks will return False with .Value < 0, there's no need to test anything else.
Option Explicit
Sub DelNegNumRows()
Dim I As Long
For I = 250 To 1 Step -1
With Cells(I, 1)
If .Value < 0 Then
.EntireRow.Delete
End If
End With
Next I
End Sub
Depending on the characteristics of your range, you may not need to check all 250 rows (although if that is the size of your database, you won't perceive a speed advantage to making the range smaller). For example, if all of your rows with data are non-blank, you can do something like:
lastrow = cells(1,1).end(xldown).row
or, if there might be blanks, and you want to find the last row in column A that has any data, something like:
lastrow = cells(rows.Count,1).end(xlup).row
You could then cycle, in the macro above:
for I = lastrow to 1 step -1
Sub Button1_Click()
Dim I As Long
Dim lastrow As Long
lastrow = Cells(Rows.Count, 1).End(xlUp).Row
For I = lastrow To 1 Step -1
With Cells(I, 2)
If .Value < 0 Then
.EntireRow.Delete
End If
End With
Next I
End Sub

Resources