what i want to do is to delete rows if there are 2 consecutive empty rows and also to have the empty rows between the header and the first set of data row to be deleted as well.This is my original
input and what i want to have is this. i have tried to find some codes here and there and come up with this code.
Sub Testing()
Dim i As Long , lRow As Long
Dim ws As Worksheet
Set ws = Activesheet
With ws
With .Range("C:C")
fr = .Find(what:="*", after:=.Cells(1, 1), LookIn:=xlValues).row
If fr > 2 Then
.Rows("2:" & fr - 1).EntireRow.Delete
End If
End With
i = 1
For i = 1 To lRow
If IsEmpty(Cells(i, 3)) And IsEmpty(Cells(i + 1, 3)) Then
.Rows(i).EntireRow.Delete
End If
Next i
End With
End Sub
However, there are still some consecutive empty rows in the middle of the data set. I know that is because i am increasing i which will look at the next cell but i am not sure how to solve it. I am new to vba and even newer to SO posting so let me know if there is anything i am doing wrong and thank you for your help.
The only thing you need to do is looping backwards. Instead of
For i = 1 To lRow
do
For i = lRow To 1 Step -1
This is because looping from the bottom doesn't have any influence on the row counting of the not yet processed rows, but looping top to bottom does.
Also you can skip i = 1 right before For it doesn't have any influence since For starts with whatever i is specified as lower bound.
I think your code is just an example but just in case note that lRow is never set to a value in your code and therefore is 0.
Note that in this line
If IsEmpty(Cells(i, 3)) And IsEmpty(Cells(i + 1, 3)) Then
your Cells objects are not referenced to the sheet of the With statement because you forgot the . in the beginning. It should be
If IsEmpty(.Cells(i, 3)) And IsEmpty(.Cells(i + 1, 3)) Then
Furthermore I highly recommend that if you use the Range.Find method
fr = .Find(what:="*", after:=.Cells(1, 1), LookIn:=xlValues).row
that you always specify the LookAt parameter as xlWhole or xlPart (see XlLookAt). Because the the LookAt parameter has no default value (sadly) and if you don't specify it, VBA will use either xlWhole or xlPart whatever was used last by either the user interface or VBA. So you cannot know which one was used before and it will become pretty random (or your code might sometimes work and sometimes not).
Alternative (much faster) approach …
… is to keep the forward loop and collect all rows to delete in a variable RowsToDelete to delete them in the end at once. It is so much faster because every delete action takes time and in this approach you only have one delete action … versus one delete action per row in the other approach.
Dim RowsToDelete As Range
For i = 1 To lRow 'forward loop is no issue here because we just collect
If IsEmpty(.Cells(i, 3)) And IsEmpty(.Cells(i + 1, 3)) Then
If RowsToDelete Is Nothing Then 'first row
Set RowsToDelete = .Rows(i).EntireRow
Else 'append more rows with union
Set RowsToDelete = Application.Union(RowsToDelete, .Rows(i).EntireRow)
End If
End If
Next i
'delete all collected rows (after the loop, so delete doesn't affect row counting of the loop)
If Not RowsToDelete Is Nothing Then
RowsToDelete.Delete
End If
I think you need to decrease i after deleting a row.
For i = 1 To lRow
If IsEmpty(Cells(i, 3)) And IsEmpty(Cells(i + 1, 3)) Then
.Rows(i).EntireRow.Delete
i = i - 1
lRow = lRow - 1
End If
If i > lRow Then Exit For
Next i
Dim blankCtr As Integer
blankCtr = 0
With ActiveSheet
For i = .Range("A" & Rows.Count).End(xlUp).Row To 2 Step -1
If Application.WorksheetFunction.CountA(Rows(i)) = 0 Then
blankCtr = blankCtr + 1
If .Rows(i).Cells(1).End(xlUp).Row = 1 Then
.Rows(i & ":" & .Rows(i).Cells(1).End(xlUp).Offset(1).Row).Delete
Exit Sub
End If
If blankCtr > 1 Then
.Rows(i).Delete
blankCtr = blankCtr - 1
End If
Else
blankCtr = 0
GoTo here
End If
here:
Next i
End With
Related
The last two days I've been trying to get the resize vba to work.
I need 3 columns (Q,R,S) to be copied and pasted after column 19. This has to happen until the number of 3 column sets (i, copies of Q:S) is equal to the value in cell ("C18"), likewise, if the number of repeats of QRS is greater than the value in C18 the unnecessary copies should be deleted.
The resize worked fine when it was just one column but now that I try to get a set of 3 added or deleted it goes wrong..the number of copies is not equal to the value in ("C18") and the number of copies made or deleted is not constant when I rerun the sub.
Does anyone have a solution?
Sub resize()
Dim SLastCol As Long
Dim i As Long
i = Range("C18").Value * 3
SLastCol = Cells(1, Columns.Count).End(xlToLeft).Column - 19
If SLastCol < i Then
Columns("Q:S").EntireColumn.copy
Columns("T").EntireColumn.Resize(, Abs(SLastCol - i)).Insert shift:=xlToRight
ElseIf SLastCol > i Then
Columns("T:W").EntireColumn.Resize(, Abs(SLastCol - i)).Delete shift:=xlToLeft
End If
Application.CutCopyMode = False
End Sub
Please, test the next code. It will copy all columns in the range colsRng, as many times as is written in "C8":
Sub resizeColumnsCopy()
Dim i As Long, colsRng As Range, lastCol As Long, rngDel As Range, arrCols, arrPrevCols
'identify the previous processed columns and delete them, if any
lastCol = cells(1, Columns.count).End(xlToLeft).Column
arrPrevCols = Range(cells(1, 20), cells(1, lastCol)).Value 'place the headers after column 20 in an array
arrCols = Range("Q1:S1").Value 'do the same with the copied columns headers
For i = 1 To UBound(arrPrevCols, 2) Step 3 'iterate in the larger array, from three to three columns
If arrPrevCols(1, i) = arrCols(1, 1) Then 'finding the first column header
If rngDel Is Nothing Then
Set rngDel = Range(cells(1, 19 + i), cells(1, 19 + i + 2)) 'create a range of the three involved columns
Else
Set rngDel = Union(rngDel, Range(cells(1, 19 + i), cells(1, 19 + i + 2))) 'careate a Union between the previous range and the next three
End If
End If
Next i
If Not rngDel Is Nothing Then rngDel.EntireColumn.Delete 'if cases of processed columns found, then delete the columns
i = Range("C18").Value
Set colsRng = Columns("Q:S")
colsRng.Copy
cells(1, colsRng.Column + colsRng.Columns.count).EntireColumn.resize(, i * colsRng.Columns.count).Insert Shift:=xlToRight
Application.CutCopyMode = False
End Sub
But, please edit your question and explain about the necessity of previous processed columns deletion. Otherwise, somebody else looking to my code will think that I recently hit my head...
I've been looking for a solution to using .PasteSpecial more than once in a an excel vba macro.
I have reports that can sometimes have thousands of rows. In these reports are two columns, I need to merge these columns so that if one column has blanks, I want the value from the second column; otherwise, just keep the value in the first column. I need to apply this twice in two different places. This is all tucked into a larger code.
My solution is to utilize .PasteSpecial with "Skip Blanks". It is quick for excel to process, much faster than looping row by row. The problem is that the code keeps crashing excel.
After debugging, here is what I've learned so far:
*The first .PasteSpecial always works, but when it gets to the second .PasteSpecial it always fails.
*I've tried STOP after the first .PasteSpecial then step through the code, and after I step through the second.PasteSpecial the code works just fine.
*If I step through the second .PasteSpecial it works like nothing is wrong - but if I just run the code like normal it crashes.
*I switched the order of the two .PasteSpecials within the code. When I do this, it no longer crashes on the problematic .PasteSpecial, but it does crash on the originally working .PasteSpecial.
Based on this, I know the problem is Excel doesn't like .PasteSpecial twice in a code. Still cannot find a work around. I've tried emptying the clip board, and I don't know enough how to set up an array let alone if that is efficient for this much data. Anybody know of a solution or work around?
Here is my .PasteSpecial code:
MainSheet.Range("N:N").Copy
MainSheet.Range("P:P").PasteSpecial Paste:=xlPasteAll, Operation:=xlNone, SkipBlanks:=True, Transpose:=False
MainSheet.Range("R:R").Copy
MainSheet.Range("Q:Q").PasteSpecial Paste:=xlPasteAll, Operation:=xlNone, SkipBlanks:=True, Transpose:=False
Your issue is that you are committing CPU to do OS level tasks when you don't actually need to paste anything.
The cells have values... so make them Equal and they... will... be...
Range("C1").Value = Range("A1").Value
Alternatively you can use a power query to just do this where Table 2 Col2 is null and ID = ID
Not needing format, please test the next code. It uses an array, not uses clipboard and is faster. If no need to exist a correspondence between the rows in the two filled columns, you can use the next fast way:
Sub copyColumnsArray()
Dim lastR As Long, arrCopy, arrFin, i As Long, k As Long
lastR = MainSheet.Range("N" & rows.count).End(xlUp).row
arr = MainSheet.Range("N1:N" & lastR).value
'fill another array only with non empty values:__________________
ReDim arrFin(UBound(arr) To 1): k = 1
For i = 1 To UBound(arrCopy)
If arrCopy(i, 1) <> "" Then arrFin(k, 1) = arrCopy(i, 1): k = k + 1
Next i
ReDim Preserve arrFin(k - 1 To 1)
'______________________________________________________
MainSheet.Range("P1").Resize(UBound(arrFin), 1).value = arrFin
lastR = MainSheet.Range("R" & rows.count).End(xlUp).row
arr = MainSheet.Range("R1:R" & lastR).value
'fill another array only with non empty values:__________________
ReDim arrFin(UBound(arr) To 1): k = 1
For i = 1 To UBound(arrCopy)
If arrCopy(i, 1) <> "" Then arrFin(k, 1) = arrCopy(i, 1): k = k + 1
Next i
ReDim Preserve arrFin(k - 1 To 1)
'______________________________________________________
MainSheet.Range("Q1").Resize(UBound(arrFin), 1).value = arrFin
End Sub
EDIT:
A better answer that worked more reliably than my original found solution is below. This is an adaptation of some advice from FaneDuru's answer. This solution is more taxing on resources; however, for now - it performs the task reliably (without crashing). I would love for there to be a better answers than row looping; however, this does answer my OP. Thank you for all the help!
Sub copyColumnsArray()
Dim lastR As Long, arrCopy
lastR = MainSheet.Range("N" & rows.count).End(xlUp).row
arrCopy = MainSheet.Range("N1:N" & lastR).value
Dim ArrayIndex as Variant
Dim RowCount as String
RowCount = 1
For Each ArrayIndex in arrCopy
If ArrayIndex = "" then
RowCount = RowCount +1
'Skip Blank
else
MainSheet.Range("P"+RowCount).value = ArrayIndex
RowCount = RowCount + 1
end if
Next
lastR = MainSheet.Range("R" & rows.count).End(xlUp).row
arrCopy = MainSheet.Range("R1:R" & lastR).value
RowCount = 1
For Each ArrayIndex in arrCopy
If ArrayIndex = "" then
RowCount = RowCount +1
'Skip Blank
else
MainSheet.Range("Q"+RowCount).value = ArrayIndex
RowCount = RowCount + 1
end if
Next
End Sub
I'm a complete novice at macro programming in excel. I have an excel file with over 1000 lines of data and would like to delete the entire row when the data in column 44 is repeated in consecutive rows. I've attached the script that I've been trying to tweak to no avail.
Currently the script deletes most of the duplicate rows but not all and I can't figure out why.
Sub deleteDupes()
Dim i As Long
With Sheets("Sheet1")
For i = .Cells(Rows.Count, 1).End(xlUp).row To 2 Step -1
If .Cells(i, 44).Value = .Cells(i - 1, 44).Value Then _
.Rows(i).Delete
Next i
End With
End Sub
You should delete the rows all at once rather than at each line. You can use the UNION method to accomplish this. This WILL successfully do what you are describing. If you have issues it has to do with your data.
More importantly, this will make your procedure one MUCH faster.
Sub deleteDupes()
Dim i As Long
With Sheets("Sheet1")
Dim killRng As Range
Set killRng = Rows(Rows.Count)
For i = .Cells(Rows.Count, 1).End(xlUp).Row To 2 Step -1
If .Cells(i, 44).Value = .Cells(i - 1, 44).Value Then _
Set killRng = Union(Rows(i), killRng)
Next i
End With
killRng.EntireRow.Delete
End Sub
Have a few questions about the following code that compressed and reformats a list.
I set variable endIndicator as a temporary marker for the end of the list. Would it be better to just continually check for the current end of the list via my ActiveSheet.UsedRange.Rows.Count + ActiveSheet.UsedRange.Rows(1).Row - 1?
I tried a With ActiveCell.Offset(rowOffset) statement in the delete loop which gives me a
run time error 424 Object Required
on the second iteration of the loop. I gather this has to do with the destruction of the previous row. Is there an appropriate With statement to use for this loop?
Similarly, I tried a With ActiveCell.EntireColumn for the last three statements and get the same result. I assume for the same reason. Is there an appropriate solution?
Here's the code
Option Explicit
Sub Condense1()
'Purpose : Condense list by removing unwanted rows
'Requires: Column B row verbiage
' Column A row blank for unwanted row
'Returns : Single compressed column of values wanted
Dim endIndicator As String
Dim rowOffset As Long
Worksheets(1).Activate 'Select Sheet
Range("A1").Select 'Set offset base
endIndicator = "zzzendozx" 'Assign unique value unlikely to be duplicated
'Find last used row
rowOffset = ActiveSheet.UsedRange.Rows.Count _
+ ActiveSheet.UsedRange.Rows(1).Row - 1
'Temporarily mark next row as loop terminator
ActiveCell.Offset(rowOffset, 0).Value = endIndicator
rowOffset = 0 'Reset offset pointer
'For each row from top to loop terminator
Do While ActiveCell.Offset(rowOffset).Value <> endIndicator
' Delete rows whose column "A" is empty
If Len(ActiveCell.Offset(rowOffset).Value) < 1 Then
ActiveCell.Offset(rowOffset).EntireRow.Delete
Else
rowOffset = rowOffset + 1 'Otherwise prepare to look at next row
End If
Loop
ActiveCell.Offset(rowOffset).EntireRow.Delete 'Remove loop terminator row
ActiveCell.EntireColumn.Delete 'Remove Column A
ActiveCell.EntireColumn.Font.Size = 14 'Set Font
ActiveCell.EntireColumn.AutoFit 'Set optimum column width
End Sub
This may be of some help:
Sub Kompressor()
Dim nLastRow As Long, nFirstRow As Long, i As Long
Set r = ActiveSheet.UsedRange
nLastRow = r.Rows.Count + r.Row - 1
nFirstRow = r.Row
For i = nLastRow To nFirstRow Step -1
With Cells(i, 1)
If .Value = "" Then .EntireRow.Delete
End With
Next i
End Sub
Note we run the loop backwards.The code will run slightly faster if we delete only one time.
I have only one column of data. I need to write a macro that would go through all the values and delete all rows that contain the word "paper".
A B
1 678
2 paper
3 3
4 09
5 89
6 paper
The problem is that the number of rows is not fixed. Sheets may have different number of rows.
Here is another simple macro that will remove all rows with non-numeric values in column A (besides row 1).
Sub DeleteRowsWithStringsInColumnA()
Dim i As Long
With ActiveSheet '<~~ Or whatever sheet you may want to use the code for
For i = .Cells(.Cells(.Rows.Count, 1).End(xlUp).Row, 1).Row To 2 Step -1 '<~~ To row 2 keeps the header
If IsNumeric(.Cells(i, 1).Value) = False Then .Cells(i, 1).EntireRow.Delete
Next i
End With
End Sub
If you're confident that the rows in question would always contain "paper" specifically and never any other string, you should match based on the value paper rather than it being a string. This is because, particularly in Excel, sometimes you may have numbers stored as strings without realizing it--and you don't want to delete those rows.
Sub DeleteRowsWithPaper()
Dim a As Integer
a = 1
Do While Cells(a, 1) <> ""
If Cells(a, 1) = "paper" Then
Rows(a).Delete Shift:=xlUp
'Row counter should not be incremented if row was just deleted
Else
'Increment a for next row only if row not deleted
a = a + 1
End If
Loop
End Sub
The following is a flexible macro that allows you to input a string or number to find and delete its respective row. It is able to process 1.04 million rows of simple strings and numbers in 2.7 seconds.
Sub DeleteRows()
Dim Wsht As Worksheet
Dim LRow, Iter As Long
Dim Var As Variant
Var = InputBox("Please specify value to find and delete.")
Set Wsht = ThisWorkbook.ActiveSheet
LRow = Wsht.Cells(Rows.Count, 1).End(xlUp).Row
StartTime = Timer
Application.ScreenUpdating = False
With Wsht
For Iter = LRow To 1 Step -1
If InStr(.Cells(Iter, 1), Var) > 0 Then
.Cells(Iter, 1).EntireRow.Delete
End If
Next Iter
End With
Application.ScreenUpdating = True
Debug.Print Timer - StartTime
End Sub