I know this is a common problem people have when trying to delete rows when looping through ranges but that is not the case here. Rather than deleting the rows, I am cutting the entire row and pasting into another worksheet, leaving a blank row. The issue is that the For Each ... Loop acts as if I deleted the row and skips the next cell in the loop. Below is a snippet of the code.
last_row = 100
Set search_rng = n_ws.Range(n_ws.Range("L2"), n_ws.Cells(last_row, 12))
For Each cell In search_rng
find_amt = cell.Value * -1
Set s_cell = search_rng.Find(find_amt, LookIn:=xlFormulas)
If Not s_cell Is Nothing Then
' do stuff
Else
'No matching value found, move row to o_ws
paste_row = o_ws.UsedRange.Rows.Count + 1
n_ws.Rows(cell.Row).Cut o_ws.Cells(paste_row, 1).EntireRow
End If
Next
Anyone know what I'm doing wrong here? I imagine I can fix it by changing the For Each ... Loop to a For i in Range loop but I'm more curious as to why this is happening.
Cutting the row still deletes it in pratice. You can convince yourself of this by just manually doing so on the spread sheet. You will see a shift in range which means this is the same issue as deleting rows in a loop. A common way to avoid this issue would be to switch to For i loop and loop backwards.
Since you are not actually deleting rows here you could also try the following....
Using your same code, you can just copy & paste the row, not cut, and then circle back around and clear the rows content to leave a blank row in place.
Else
paste_row = o_ws.UsedRange.Rows.Count + 1
n_ws.Rows(Cell.Row).Copy o_ws.Cells(paste_row, 1)
n_ws.Rows(Cell.Row).ClearContents
End If
Next cell
Note
The better way to do this would be to loop through your range and create a Union (collection of cells) that meet your criteria. Once the loop is done you can copy, paste, & clear the Union all at once. This means you have one instance of actions taken to worksheet rather many
Related
What I'm trying to do is very simple, I just don't have much VBA / Excel experience to know how to do it.
I am working on a macro to do a few different things, I'm just stuck on this part.
What I want to do is go through all of my rows, and if the first cell in the row is empty, I want to select the entire row and UnMerge it. I know that if the first cell is empty, that row contains merged cells that I want unmerged.
I know that these two lines below will select row 2 and Unmerge it, but I need a way to loop through my data and find and unmerge the rows automatically, because the row numbers wont be the same every time.
Rows("2:2").Select
Selection.UnMerge
I want to do something like this:
For (each row) {
if (the first cell is empty) {
UnMerge all cells in that row;
}
}
I just don't know how to do that in VBA syntax.
Any help is appreciated!
This should do the job.
Sub UnmergeRows()
Dim R As Long ' loop counter: rows
With Worksheets("Sheet1") ' change tab name as required
' "B" should be the longest column in the worksheet
For R = 2 To .Cells(.Rows.Count, "B").End(xlUp).Row
If IsEmpty(.Cells(R, 1)) Then .Rows(R).MergeCells = False
Next R
End With
End Sub
I was wondering where I am going wrong, I basically want to apply an auto filter on an excel sheet i'm opening through vb.net and then read through the filtered data.
I have read almost every vb/autofilter related answer but I must be doing something terribly wrong but just can seem to spot it!
xlWorkSheet = xlWorkBook.Sheets(2)
xlWorkSheet.Name = "ACC"
xlWorkSheet.Range("$A$4:$AE$4480").AutoFilter(Field:=31, Criteria1:="=Accepted")
xlCell = xlWorkSheet.UsedRange
intLstRowIdx = xlWorkSheet.Range("A" & xlWorkSheet.Rows.Count).End(Microsoft.Office.Interop.Excel.XlDirection.xlUp).Row
For rcnt = intLstRowIdx To 2 Step -1
Dim Obj = CType(xlCell.Cells(rcnt, 31), excel.Range)
If Obj.Text.ToString.ToUpper.Contains("ACC") = True Then
xlWorkSheet.Rows(rcnt).Delete()
End If
Next
What i would expect this to do would be to apply the filter to the excel sheet and then basically mkae all my records/rows be ones in which the column 31 has "Accepted" in it, therefore i would attempt the change the delete row part to a much quicker delete such as delete range or that etc.
But when i run the above code it runs and all but it doesn't seem to keep or even run the filter as "Rejected" records are still showing up in my for loop etc.
I appreciate any help guys, I really have tried loads and just know it is me being dumb and missing out on a single line or contradicting myself within my code.
edit
I have used excel to record the macro of me performing the filter in excel and the line xlWorkSheet.Range("$A$4:$AE$4480").AutoFilter(Field:=31, Criteria1:="=Accepted") is what I was given.
The fast method is to use SpecialCells to get all the filtered range. After getting it you can delete all rows in one line:
Dim rngTable, rngData, rngVisible As Excel.Range
'// Original data
rngTable = xlWorkSheet.Range("$A$4:$AE$4480")
'// Exclude header - we don't want it to take a part in filtering
With rngTable
'// Offset range by one row and then resize it excluding last row.
'// This way we obtain all cells without header.
rngData = rngTable.Offset(1).Resize(.Rows.Count - 1)
End With
'// Filtering
rngData.AutoFilter(Field:=31, Criteria1:="=Accepted")
Try
'// SpecialCells takes all visible cells (i.e. filtered), which is what we need.
'// We need to use it in Try..Catch because if there are no filtered cells,
'// this method will throw exception.
rngVisible = rngData.SpecialCells(Excel.XlCellType.xlCellTypeVisible)
'// Having obtained all cells, delete entire rows.
rngVisible.EntireRow.Delete()
Catch ex As Exception
'// We're here 'cause no rows were filtered
End Try
If A1 is not empty (contains actual data), is there a way to if-then so that I can insert a blank row under A1. This should apply to any cell/row as I have a spreadsheet with about 65000 rows and they want to have blanks to separate the rows that contain different identifiers. I looked on here, right before I signed up, and I saw some things about empty cells or making cells empty depending on other cells, but that doesn't seem to be what I'm looking for. Google wasn't too much help either.
thanks.
Is this what you want?
Sub helping()
Dim count As Long
For count = ActiveSheet.UsedRange.Rows.count To 1 Step -1
If Information.IsEmpty(Cells(count, 1)) = False Then Rows(count + 1).Insert
Next count
End Sub
Is it possible to use a function in Excel 2003 to delete an entire row if it's the same as the previous one? For example:
apple
apple
plum
vinegar
apple
banana
banana
banana
apple
I want to delete #2, 7# and #8, but I don't want #5 and #9 to be deleted. I want to delete a duplicated entry ONLY IF it's the next one. I hope I managed to keep it clear to you.
If there isn't a function, how can I do that in VBA? Thanks in advance!
If it is a one-off, you can do it without VBA fairly simple by adding a formula in the next column - let's say your column is A:
in B2 put the following:
=IF(A1=A2,"DELETE","")
drag the formula down
add an autofilter on both columns
filter on "DELETE" in column B
delete all visible rows
remove column B
Here's something that might fit, deleting all duplicate rows (physically, not only cell data!), so keep in mind that it can be used only if you use one-column sheets. Otherwise you will loose some other data.
Sub Unduplicate()
Dim prev As String
Dim sel As Range
Dim i As Integer
Set sel = Range(Selection, Selection)
prev = sel.Offset(0).Value ' set prev as the first value - never will be deleted
i = 1 ' start from 2nd row
Do While sel.Offset(i).Value <> "" And sel.Offset(i).Value <> ""
If sel.Offset(i).Value = prev Then ' if duplicate - delete row
sel.Offset(i).EntireRow.Delete
Else ' else set new prev, and go further
prev = sel.Offset(i).Value
i = i + 1
End If
Loop
End Sub
After running this macro for your example, I'm getting:
You can modify it, to store the values in an array, and than fill the column with remembered array instead of deleting the rows, but that should be easy now.
Just a quick note...Make sure you work your way from the bottom of the range if you are deleting entire rows. You may get some unexpected results if you work your way down from the top of the range. You may also want to consider clearing the cell value and then sorting, instead of deleting. I would write up an example for you but I am short on time. If you get stuck I can write it for you later.
Edit:
My original answer is not necessarily true as shown in makciook's solution below. In the past I have mistakenly approached the problem this way (DO NOT USE THIS AS A SOLUTION!!!):
Sub duplicates()
Dim c As Range, rng As Range
Set rng = Selection ''Select the entire list before running
For Each c In rng
If c.Value = c.Offset(-1, 0).Value Then c.EntireRow.Delete
Next
End Sub
With this script, the cell range does not reset once a row is deleted and shifted up. So running this would not catch the second duplicate of banana in your list. As an alternative I usually find the last row and work my way up to the first row to account for the rows shifting up when a deletion occurs. I am going to give myself a -1 if possible.
I have this piece of code which finds the excel row of an item from a list and deletes the items from a list. What I want... is to delete the Excel row as well.
The code is here
Private Sub imperecheaza_Click()
Dim ws As Worksheet
Dim Rand As Long
Set ws = Worksheets("BD_IR")
Rand = 3
Do While ws.Cells(Rand, 4).Value <> "" And Rand < 65000
If ws.Cells(Rand, 4).Value = gksluri.Value * 1 And ws.Cells(Rand, 5).Value = gksluri.List(gksluri.ListIndex, 1) * 1 Then
ws.Range(Rand, 1).EntireRow.Delete '(here I want to delete the entire row that meets the criteria from the If statement)
gksluri.RemoveItem gksluri.ListIndex
Exit Do
End If
Rand = Rand + 1
Loop
End Sub
Where I added ws.Range(Rand,1).EntireRow.Delete is where I want to delete the entire row but I don't know how to do it. What I want... if it finds the same value in a cell like in some selected item of my list to be able to remove both the entire row in excel and the item from the listbox. It works to remove the item from the listbox but I don't know how to remove the row as well
Chris Nielsen's solution is simple and will work well. A slightly shorter option would be...
ws.Rows(Rand).Delete
...note there is no need to specify a Shift when deleting a row as, by definition, it's not possible to shift left
Incidentally, my preferred method for deleting rows is to use...
ws.Rows(Rand) = ""
...in the initial loop. I then use a Sort function to push these rows to the bottom of the data. The main reason for this is because deleting single rows can be a very slow procedure (if you are deleting >100). It also ensures nothing gets missed as per Robert Ilbrink's comment
You can learn the code for sorting by recording a macro and reducing the code as demonstrated in this expert Excel video. I have a suspicion that the neatest method (Range("A1:Z10").Sort Key1:=Range("A1"), Order1:=xlSortAscending/Descending, Header:=xlYes/No) can only be discovered on pre-2007 versions of Excel...but you can always reduce the 2007/2010 equivalent code
Couple more points...if your list is not already sorted by a column and you wish to retain the order, you can stick the row number 'Rand' in a spare column to the right of each row as you loop through. You would then sort by that comment and eliminate it
If your data rows contain formatting, you may wish to find the end of the new data range and delete the rows that you cleared earlier. That's to keep the file size down. Note that a single large delete at the end of the procedure will not impair your code's performance in the same way that deleting single rows does
Change your line
ws.Range(Rand, 1).EntireRow.Delete
to
ws.Cells(Rand, 1).EntireRow.Delete
Better yet, use union to grab all the rows you want to delete, then delete them all at once. The rows need not be continuous.
dim rng as range
dim rDel as range
for each rng in {the range you're searching}
if {Conditions to be met} = true then
if not rDel is nothing then
set rDel = union(rng,rDel)
else
set rDel = rng
end if
end if
next
rDel.entirerow.delete
That way you don't have to worry about sorting or things being at the bottom.
Something like this will do it:
Rows("12:12").Select
Selection.Delete
So in your code it would look like something like this:
Rows(CStr(rand) & ":" & CStr(rand)).Select
Selection.Delete