Excel VBA delete rows based on multiple column criteria - vba

I am trying to do some cuts to a sheet of data based on if a row meets 2 criteria in different columns, i.e. if the value in column D is > -2 and if the value in the adjacent cell of column F is > -2 or NA, then delete the entire row. If only 1 or none of the criteria is met then it should keep the row. Below is what i have so far. When i run the macro, it will go on forever, but I don't see how this should be since it doesn't look like an endless loop to me (to be fair i have only let it sit for 45 minutes, but there is only around 15,000 data rows so it shouldn't take longer than 10 minutes realistically). Any help would be greatly appreciated.
Sub Cuts()
Dim wb1 As Workbook, sh1 As Worksheet
Dim lastrow1 As Long, lastrow2 As Long
Set wb1 = Workbooks(“ovaryGisticARRAYRNAseq.final.xlsx")
Set sh1 = wb1.Sheets(“Cuts”)
lastrow1 = sh1.Cells(Rows.Count, 4).End(xlUp).Row
lastrow2 = sh1.Cells(Rows.Count, 6).End(xlUp).Row
For i = 1 To lastrow1
For j = 1 To lastrow2
If sh1.Cells(i, 4).Value > -2 Then
If sh1.Cells(j, 6).Value > -2 Then
sh1.Cells(j, 6).EntireRow.Delete
ElseIf sh1.Cells(j, 6).Value = “NA” Then
sh1.Cells(j, 6).EntireRow.Delete
End If
End If
Next j
Next i
End Sub

I'm not sure how you want to handle blank cells or text in column headings but I would propose this modification.
Sub Cuts()
Dim wb1 As Workbook
Dim lr As Long, i As Long
Set wb1 = Workbooks(“ovaryGisticARRAYRNAseq.final.xlsx")
With wb1.Sheets("Cuts")
lr = Application.Max(.Cells(Rows.Count, 4).End(xlUp).Row, _
.Cells(Rows.Count, 6).End(xlUp).Row)
For i = lr To 1 Step -1
If .Cells(i, 4).Value > -2 And _
(.Cells(i, 6).Value > -2 Or UCase(.Cells(i, 6).Value) = "NA") Then
.Rows(i).EntireRow.Delete
End If
Next i
End With
End Sub

Related

quicker way for loop inside loop

I have a working code for copying large amount of data from a monthly sheet to daily sheet. There are about 30 columns of data to be copied over 2000 rows. I don't know how I can speedup the job as it is taking about 3 minutes to copy even one column. I have to this for all 30 columns. The order of columns in monthly sheet is not same as in daily sheet; for eg. monthly sheet column D might represent column P of daily sheet. If the learned ones can help to improve the code I will be most grateful.
PJ
Sub COPY2()
Dim i As Long, j As Long, lastrow1 As Long, Lastrow2 As Long, myname As String
Dim SWB As Workbook, TWB As Workbook, Sws As Worksheet, Tws As Worksheet
Set SWB = ActiveWorkbook
Set Sws = SWB.Sheets("SHEET1")
Windows("DAILY.xlsX").Activate
Set TWB = ActiveWorkbook
Set Tws = TWB.Sheets("Sheet1")
lastrow1 = Sws.Range("A" & Rows.Count).End(xlUp).Row
Lastrow2 = Tws.Range("A" & Rows.Count).End(xlUp).Row
Sws.Activate
For i = 2 To lastrow1
myname = Sws.Cells(i, "B").Value
Tws.Activate
For j = 2 To Lastrow2
If Tws.Cells(j, "D").Value = myname Then Tws.Cells(j, "P").Value = Sws.Cells(i, "D").Value
If Tws.Cells(j, "P").Value = Sws.Cells(i, "D").Value Then Exit For
Next j
Next i
End Sub
Excel operations take much time. Try to access data in blocks rather than cell by cell.
BTW activating sheets are useful while testing and debugging but it wastes your time when running the app live.
Try the snippet below. The concept is to read and manipulate data to buffers, and write back Excel in one single step when finished processing, so you can save thousends of Excel operations.
Dim sa(), tad(), tap()
lastrow1 = sws.Range("A" & Rows.Count).End(xlUp).Row
lastrow2 = tws.Range("A" & Rows.Count).End(xlUp).Row
sws.Activate
sa = Range(sws.Cells(1, 2), sws.Cells(lastrow1, 4)) ' B:D columns
tad = Range(tws.Cells(1, "D"), tws.Cells(lastrow2, "D")) ' D column
tap = Range(tws.Cells(1, "P"), tws.Cells(lastrow2, "P")) ' P column
For i = 2 To lastrow1
For j = 2 To lastrow2
If tad(j, 1) = sa(i, 1) Then
tap(j, 1) = sa(i, 3)
Exit For
End If
Next j
Next i
Range(tws.Cells(1, "P"), tws.Cells(lastrow2, "P")) = tap
End Sub

Color a column by searching a particular text in A:A

Assist me with a VBA to color column from A:G, by searching a specific text, say 'UK'in Column A
Try this. Code1 works on every sheet in the workbook but if you like for this to work for the active sheet then try Code2. For Code2 remember to write the correct sheets name. Hope it helps.
Here you can choose an index color: https://stackoverflow.com/a/25000926/7238313
Code1:
Sub rowhighlight()
Dim sht As Worksheet
Dim nlast As Long
For Each sht In ActiveWorkbook.Worksheets
sht.Select
nlast = Cells(Rows.Count, "A").End(xlUp).Row
For n = nlast To 2 Step -1
If sht.Cells(n, "A").Value = "UK" Then
sht.Range("A" & n, "G" & n).Interior.ColorIndex = 37
'different color number place here----------------^
End If
Next n
Next sht
End Sub
Code2:
Sub rowhighlight()
Dim nlast As Long
Sheets("sheetname").Activate
Set sht = ActiveWorkbook.ActiveSheet
nlast = Cells(Rows.Count, "A").End(xlUp).Row
For n = nlast To 2 Step -1
If sht.Cells(n, "A").Value = "UK" Then
sht.Range("A" & n, "G" & n).Interior.ColorIndex = 37
'different color number place here----------------^
End If
Next n
End Sub
a very short and dirty and rude code is the following:
Sub ColorColumns()
On Error Resume Next
Range("A1", Cells(Rows.Count, 1).End(xlUp)).Find(what:=Application.InputBox("Text to search:", , , , , , , 2), LookIn:=xlValues, lookat:=xlWhole).Resize(, 7).Interior.ColorIndex = 6
End Sub
You can use the following code. Remember to keep the sheet containing the table active when running the code. Alternatively, you can specify the sheet explicitly when setting the ws variable.
Sub SelectivelyColorARowRed()
Dim ws As Worksheet
Set ws = ActiveSheet
lng_lastrow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim k As Long
For k = 2 To lng_lastrow
If Trim(ws.Cells(k, 1).Value) = "UK" Then
'you can also apply UCase if you want the search to be case-insensitive
'like: If UCase(Trim(ws.Cells(k, 1).Value)) = UCase("Uk") Then
ws.Range(Cells(k, 1), Cells(k, 7)).Interior.ColorIndex = 22
End If
Next k
End Sub

VBA to copy x (variable) rows to another worksheet

Gods of VBA,
Have been trying all morning to tweak this piece of amateur-VBA (in which case i'm the amateur) to perform as i want.
What is does now is the following; Looks for cell value 1 in Column O on the third sheet in my workbook. When it gets a hit, it copies the row which has 1 in Column O to a new worksheet called "Blad1". It then switches back to my 3rd sheet in the workbook "Doorvoeren".
It will loop and perform the task as wanted, only thing i can't get it to do is copy rows based on a variable in sheet "Doorvoeren". When this value is 5, i want it to copy the row with 1 in column O, and the 4 rows below it. (as example).
Could you please send me in the right direction here? Trying to make it work, but also learn from it in the process.
My code is added in the sample below:
Sub testIt()
Dim r As Long, endRow As Long, pasteRowIndex As Long
endRow = 500
pasteRowIndex = 5
For r = 3 To endRow
If Cells(r, Columns("O").Column).Value = 1 Then
Rows(r).Select
Selection.Copy
Sheets("Blad1").Select
Rows(pasteRowIndex).Select
ActiveSheet.Paste
pasteRowIndex = pasteRowIndex + 1
Sheets("Doorvoeren").Select
End If
Next r
End Sub
EDIT: Thank you all for your answers, actually having real trouble to find one that works. To explain again; I need this VBA tweaked in the way that it looks at cell Q3, in sheet "Doorvoeren" to get the number of rows to copy. So, if Q3 is cell value; 5, i want it to to copy the row with number 1 in Column O, in Sheet "Doorvoeren", but also the other four rows below it.
So my 1 in Column O, is just a marker, not the number of rows i want to copy.
Please ask/tell me if i'm not being totally clear.
Here is my solution (slightly ammending your code with annotations)
Sub testIt()
'add another variable (called var)
Dim r As Long, endRow As Long, pasteRowIndex As Long, Var As Long
endRow = 500
pasteRowIndex = 5
For r = 3 To endRow
If Cells(r, Columns("O").Column).Value = 1 Then
'Grab the var number from the Doorvoeren sheet. Var will then determine how many rows need to be copied in each circumstance
Sheets("Doorvoeren").Select
Var = Cells(r, Columns("Q").Column).Value
Rows(r & ":" & r + (Var - 1)).Select
Selection.Copy
Sheets("Blad1").Select
Rows(pasteRowIndex).Select
ActiveSheet.Paste
pasteRowIndex = pasteRowIndex + Var
Sheets("Doorvoeren").Select
End If
Next r
End Sub
It's recommended if you avoid using Select and ActiveSheet, instead use referenced Sheets and Ranges.
Option Explicit
Sub testIt()
Dim r As Long, endRow As Long, pasteRowIndex As Long
Dim PasteRow As Long
With Sheets("Doorvoeren")
' find last row with data in Column "O" in "Doorvoeren" sheet
endRow = .Cells(.Rows.Count, "O").End(xlUp).Row
For r = 3 To endRow
If .Cells(r, "O").Value = 1 Then
pasteRowIndex = 1
Else
If .Cells(r, "O").Value = 5 Then
pasteRowIndex = 5
End If
End If
' find last row with data in Column "O" in "Blad1" sheet
PasteRow = Sheets("Blad1").Cells(Sheets("Blad1").Rows.Count, "O").End(xlUp).Row
' copy number of rows from "Doorvoeren" sheet to "Blad1" sheet, paste them on the first empty row in "Blad1" sheet
.Range("O" & r).Resize(pasteRowIndex).EntireRow.Copy Destination:=Sheets("Blad1").Range("A" & PasteRow + 1)
Next r
End With
End Sub
I made a slight change upon your explanation.
'====================================================
Sub testIt()
Dim r As Long, endRow As Long, pasteRowIndex As Long
Dim DestR as Range
Dim Rloop as Range
dim RowsCounter as Integer
endRow = 500
pasteRowIndex = 5
RowsCounter = 0
For Each Rloop in Sheets("Doorvoeren").range("O3:O" & endRow)
if Rloop = 1 and RowsCounter=0 then RowsCounter = Rloop.Offset(0, 2)
If Rloop = 1 or RowsCounter > 0 Then
Set DestR = Sheets("Blad1").range("A" & pasteRowIndex)
Rloop.EntireRow.Copy DestR
pasteRowIndex = pasteRowIndex + 1
RowsCounter = RowsCounter - 1
End If
Next Rloop
End Sub
Hope this helps better :)

Excel VBA - Why does this macro delete everything

I need some help with this macro. I have a workbook that is formatted pretty poorly, but consistently every time I open it. Among other things, the goal is to find the non-blank cells in column B and delete the entire 2 rows below and the 1st row above each of those populated B cells.
The first loop I have in the code works just the way I want it to, but the second loop seems to only work on the 1st instance of a populated B cell, but then it deletes everything else above it, like 500 cells worth of data.
Can someone explain to me why this is happening, and if you could find a way to combine both of those for loops into 1, that would be nice too.
Sub test()
Dim currentSht As Worksheet
Dim startCell As Range
Dim lastRow As Long, lastCol As Long
Dim colNames As Variant
Dim i As Integer, j As Integer
Set currentSht = ActiveWorkbook.Sheets(1)
Set startCell = currentSht.Range("A1")
lastRow = startCell.SpecialCells(xlCellTypeLastCell).Row
lastCol = startCell.SpecialCells(xlCellTypeLastCell).Column
For i = lastRow To 1 Step -1
If currentSht.Cells(i, "B").Value <> "" Then
currentSht.Cells(i, "B").Offset(1).EntireRow.Delete
End If
Next i
Range("D3").Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Delete Shift:=xlUp
currentSht.Rows("1:1").EntireRow.Delete
currentSht.Range("c:d, f:g, i:k").EntireColumn.Delete
currentSht.Range("A:D").Columns.AutoFit
For j = lastRow To 2 Step -1
If currentSht.Cells(j, "B").Value <> "" Then
currentSht.Range(Cells(j, "B").Offset(-1), Cells(j, "B").Offset(-3)).EntireRow.Delete
End If
Next j
End Sub
Thank you
The second loop deletes everything because upon deletion of the lines above the found value, said value gets moved up and will be found again, triggering another deletion. To fix this, the quickest way would be to skip the next two lines by modifying j:
For j = lastRow To 2 Step -1
If currentSht.Cells(j, "B").Value <> "" Then
currentSht.Range(Cells(j, "B").Offset(-1), Cells(j, "B").Offset(-3)).EntireRow.Delete
j = j - 2
End If
Next j
It really doesn't matter much if you are looping from top to bottom or vice versa. The only difference would be if there are two entries in column B near each other. In that case, the search order would determine which one is deleted. But is deletion really what you want? Maybe you could .Clear the contents of the rows instead of deleting them.
edit: here's the new code a bit cleaned up
Sub test()
Dim currentSht As Worksheet
Dim startCell As Range
Dim lastRow As Long, lastCol As Long
Dim colNames As Variant
Dim i As Integer, j As Integer
Set currentSht = ActiveWorkbook.Sheets(1)
Set startCell = currentSht.Range("A1")
lastRow = startCell.SpecialCells(xlCellTypeLastCell).Row
lastCol = startCell.SpecialCells(xlCellTypeLastCell).Column
For i = lastRow To 1 Step -1
If currentSht.Cells(i, "B").value <> "" Then
'reference the row directly
currentSht.Rows(i + 1).Delete
End If
Next i
'Do not use selection if you can avoid it
Range("D3", Range("D3").End(xlToRight)).Delete Shift:=xlUp
currentSht.Rows(1).Delete
currentSht.Range("C:D, F:G, I:K").Delete
currentSht.Range("A:D").Columns.AutoFit
For j = lastRow To 2 Step -1
If currentSht.Cells(j, "B").value <> "" Then
currentSht.Rows(j - 1).Delete
currentSht.Rows(j - 2).Delete
j = j - 2
End If
Next j
End Sub
If you want to combine the loops the behavior of the macro will change because of the deletions that happen between the loops.

Excel copy all values from sheet 1 & 2 that are highlighted/yellow to sheet 3

I have an excel workbook with 3 sheets, the first two contain lots of data and the third is blank.
I want to create a macro that copies all the highlighted/yellow cells from sheet 1 & 2 and pastes them in sheet 3.
I have some code in a macro which at the minute is only to copy sheet 1 to sheet 3 but it copies everything even though i have used If .Interior.ColorIndex
Sub Yellow()
Dim LR As Long, i As Long, j As Long
j = 1
LR = Range("A" & Rows.Count).End(xlUp).Row
For i = 1 To LR
With Worksheets("Sheet1").Range("A1:CF200" & i)
If .Interior.ColorIndex Like 27 Or 12 Or 36 Or 40 Or 44 Then
.Copy Destination:=Worksheets("Sheet3").Range("J" & j)
j = j + 1
End If
End With
Next i
End Sub
UPDATE: code below modified to skip yellow-highlighted cells that are blank...
I might break this one up into two sections, a script that does the looping through sheets and a function that checks if a cell (Range) is yellow. The code below has lots of comments which walk through the steps:
Option Explicit
Sub PutYellowsOnSheet3()
Dim Sh As Worksheet, Output As Worksheet
Dim LastRow As Long, LastCol As Long
Dim Target As Range, Cell As Range, Dest As Range
Dim DestCounter As Long
'initialize destination counter and set references
DestCounter = 1
Set Output = ThisWorkbook.Worksheets("Sheet3")
'loop through sheets that are not named "Sheet3"
For Each Sh In ThisWorkbook.Worksheets
If Sh.Name <> "Sheet3" Then
With Sh
LastRow = .Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
LastCol = .Cells.Find("*", SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
Set Target = .Range(.Cells(1, 1), .Cells(LastRow, LastCol))
End With
For Each Cell In Target '<~ loop through each cell in the target space
If AmIYellow(Cell) And Cell.Value <> "" Then '<~ blank check too
Set Dest = Output.Cells(DestCounter, 1)
Cell.Copy Dest
DestCounter = DestCounter + 1 '<~ keep incrementing on sheet 3
End If
Next Cell
End If
Next Sh
End Sub
'call this function when you'd like to check if a range is yellow
Public Function AmIYellow(Cell As Range) As Boolean
If Cell Is Nothing Then
AmIYellow = False
End If
Select Case Cell.Interior.ColorIndex '<~ this is the yellow check
Case 27, 12, 36, 40, 44
AmIYellow = True
Case Else
AmIYellow = False
End Select
End Function
Your condition
.Interior.ColorIndex Like 27 Or 12 Or 36 Or 40 Or 44
always evaluates to True (any number except 0 is True) so in fact your condition is:
'condition' Or True Or True ...
should be:
`.Interior.ColorIndex Like 27 _
Or .Interior.ColorIndex Like 12 _
Or .Interior.ColorIndex Like 36 _
Or .Interior.ColorIndex Like 40 _
Or .Interior.ColorIndex Like 44`
or better rewritten as:
Select Case .Interior.ColorIndex
case 27,12,36,40,44
'action
Case Else
'do nothing
End Select
There are several mistakes to be found in your script. I think you want to loop all the cells in the given range and copy over only the cells that have the specified colors. That could be done like this:
Sub jzz()
Dim LR As Long, i As Long, j As Long
Dim c As Range
j = 1
LR = Range("A" & Rows.Count).End(xlUp).Row
For Each c In Worksheets("Blad1").Range("A1:G" & LR)
If c.Interior.ColorIndex = 6 Then
c.Copy Destination:=Worksheets("Blad2").Range("A" & j)
j = j + 1
End If
Next c
End Sub
You will need to modify the code somewhat, for example "Blad1" will not exist in your workbook, and I took only ColorIndex = 6