Deleting rows in Excel based on Dictionary value in Excel VBA - vba

I am still getting into VBA and currently I am trying to do the following:
- Delete rows in an Excel sheet based on a value stored in a dictionary.
I have this code:
For Each Key In dict.Keys
i = Key
Cells.Rows(i).Delete
Next
where before that I have populated the dictionary. The idea is that the "key" stores all the row number in which a specific value is stored, for example "1".
With the for each I want to delete all the rows stored in the dictionary, but once you delete a row in Excel the number changes. For example the first stored value in the dictionary is 5, so the for each will delete row number 5. The second value in the dictionary is 10 and now when the for each tries to delete row 10, it delete row 10, which in the original file used to be row 11. Basically the for each fails to delete the correct rows.
As I said I am new to VBA and I am not even sure if this is the correct way to do this or there is something better than dictionaries for this situation. I am looking forward to hearing your ideas and suggestions.
Thank you in advance!
Update: Solved by Ambie and sktneer. Check answers.

You may try something like this...
Dim rng As Range
Dim it
For Each it In dict.keys
If rng Is Nothing Then
Set rng = Range("A" & it)
Else
Set rng = Union(rng, Range("A" & it))
End If
Next it
If Not rng Is Nothing Then
rng.EntireRow.Delete
End If
End Sub

I'm not sure I'd bother with the Dictionary. Afterall, a Range is a collection, so perhaps just consider putting all of your target rows into a range and run the 'bulk' delete on that range. It'd be far quicker than a delete by looping rows:
Dim delRng As Range, srcRng As Range, cell As Range
'Loop through the range containing your criterion test
With Sheet1
Set srcRng = .Range(.Cells(1, "A"), .Cells(.Rows.Count, "A").End(xlUp))
End With
For Each cell In srcRng.Cells
If cell.Value2 = 1 Then 'change to your criterion
'It's a hit so add the cell to our delete range
If delRng Is Nothing Then
Set delRng = cell
Else
Set delRng = Union(delRng, cell)
End If
End If
Next
'Delete the rows
If Not delRng Is Nothing Then delRng.EntireRow.Delete

Related

Trying to identify matching cells randomly distributed in 2 seperate Excel sheets, and have the matching data copied and pasted into a third sheet

So I have several worksheets on the same Excel workbook that I need to compare. Worksheet 1 is the masterlist, and I need to compare worksheets 1-2, 1-3, 1-4. I then need to paste any similar 1-2 data cells in column A of worksheet 5, and similar 1-3 data cells in column B of worksheet 5, and 1-4 similarities to column C of worksheet 5. For starters I have focused on getting the 1-2 comparison to work. So far I have been able to get my test number to be pasted to cell A1 of sheet 5. I am running into trouble because it only works for 1 cell, and I cannot get the program to paste a similarity in A1, and then A2... etc, when I have multiple similar items. They just overwrite each other in cell A1, or in the entire A column. I am also running into trouble because the program as it is written stops when it hits a blank space, but I need it to just skip the blanks and read the next cell when it comes across them. This is because my data sheets are very messy and the data is scattered over several thousands of rows among several different columns, with spaces randomly interjected. Below is my working code for just reading a similarity, and pasting it into A1. I should note that I have considered adding a specific cell range depending on which sheet I am on in order to put an end point on the program, but I haven't quite figured out how to work it in.
Sub findDuplicates()
' code to find duplicates in 2 different worksheets
Dim rng1, rng2, rngA, cell1, cell2 As Range
' 4 ranges have been defined
Set rng1 = Sheets("Sheet1").Range("C:C")
'rng1 defines the existing data in column C and worksheet1
Set rng2 = Sheets("Sheet2").Range("C:C")
'rng2 defines the data in column C and worksheet2
Set rngA = Sheets("Sheet5").Range("A1")
For Each cell1 In rng1
If IsEmpty(cell1.Value) Then Exit For
'check for empty rows. If true then exit the program
For Each cell2 In rng2
If IsEmpty(cell2.Value) Then Exit For
If cell1.Value = cell2.Value Then
'compare data in cell1 and cell2 and then copy/paste if they have equal values
cell1.Copy
Sheets("Sheet5").Select
rngA.Select
ActiveSheet.Paste
End If
'run the looping process
Next cell2
Next cell1
End Sub
The general idea of what I imagine the program to look like would be something like
Define ranges
Block of code that runs through each cell in sheet 1 comparing it to all cells in sheet 2.
Block of code that, when similarities are found, copy/paste that cell on sheet 1 to sheet 5 column A
*Program resumes scan from the next cell on sheet 1*
Block of code that breaks the program when it hits the end of the specified cell range
Any help with this would be greatly appreciated! You would be saving me at least a week's worth of mindless work.
A few comments about your code:
Dim rng1, rng2, rngA, cell1, cell2 As Range means only cell2 is defined As Range, while rng1, rng2, rngA, cell1 defined As Variant
You don't need to have 2 For loops to compare, you can replace the second For loop with the Match function, it will save you precious run-time.
You need to find the next empty row in "Sheet5", by using NextRow = Sheets("Sheet5").Cells(Sheets("Sheet5").Rows.Count, "A").End(xlUp).Row + 1
Last, you don't need to Select the sheets in order to copy>>paste, you can so it in 1-line (see in my code below).
Code
Sub findDuplicates()
' code to find duplicates in 2 different worksheets
' 4 ranges have been defined
Dim rng1 As Range, rng2 As Range, rngA As Range, cell1 As Range, cell2 As Range
Dim NextRow As Long
'rng1 defines the existing data in column C and "Sheet1"
Set rng1 = Sheets("Sheet1").Range("C:C")
'rng2 defines the data in column C and "Sheet2"
Set rng2 = Sheets("Sheet2").Range("C:C")
Set rngA = Sheets("Sheet5").Range("A1")
For Each cell1 In rng1
If Not IsEmpty(cell1.Value) Then ' only check non-empty cells
If Not IsError(Application.Match(cell1.Value, rng2 , 0)) Then ' <-- confirm match was asuccessful
' find next empty row in column "A" in "Sheet5"
NextRow = Sheets("Sheet5").Cells(Sheets("Sheet5").Rows.Count, "A").End(xlUp).Row + 1
' Copy >> Paste in 1 line (without need to Select the Sheets)
cell1.Copy Destination:=Sheets("Sheet5").Range("A" & NextRow)
End If
'run the looping process
End If
Next cell1
End Sub
Your problem is that rngA points to A1 and nothing changes that.
Add one line after your paste command:
ActiveSheet.Paste
Set rngA = rngA.Offset(1,0) ' This will move the pasting location one step down

Excel Database data issue

I have a farily large database of around 2000 people, sheet1 has all of their names and relevant details. Sheet 2 has data pulled on from a site. I would like the data from sheet 2 to auto populate the cells in Sheet 1. Also if the person does not exist in sheet1 to highlight the data it couldnt do. I am so stuck on this.
Sub dup()
Dim cell As Range, cella As Range, rng As Range, srng As Range
Set rng2 = Sheets(2).Range("A2:E2000")
Set rng3 = Sheets(3).Range("A2:E29000")
For Each cell In rng2
For Each cella In rng3
If cella = cell Then
cella.Interior.ColorIndex = 6
' cella.AddComment.Text Text:="duplicate value"
End If
Next cella
Next cell
Set rng2 = Sheets(2).Range("T2:Y2000")
Set rng4 = Sheets(4).Range("A1:F2000")
For Each cell In rng2
For Each cella In rng4
If cella = cell Then
cella.Interior.ColorIndex = 6
' cella.AddComment.Text Text:="duplicate value"
End If
Next cella
Next cell
End Sub
Its hard for me to show as it has a lot of columns not sure how on earth i can show you what im trying to do? :(
Try https://filetea.me/t1sfGPWECvdQqmgVDGtXL4oRQ
Maybe, if you want to do it without vba, you could use the LOOKUP function in the sheet 1's auto populate column. It works like that:
=LOOKUP(sheet1!A2, sheet2!table[a], sheet2!table[b])
This will find the value in the column "b" of the table in sheet2 based on the values of column "a". This will chose the value in the same row were column "a" matches the value in sheet1's A column. Let me know if I wasn't clear enough here.
Then you can use Conditional Formatting rules for the highlight you said. I suggest the COUNTIF function, that will return 0 if no matching value is found in the specified range.
=COUNTIF(A2:A5,A4)
This, for example, cont values in A2:A5 that matches the values in A4.
Also, you will find the conditional formatting tools in the home tab, if you are using excel 2016.
See the link for more information:
Information you may need

Delete Hidden/Invisible Rows after Autofilter Excel VBA

I guess this is pretty straight forward, but for some reason it just does not seem to work for me :(
I have the below code which auto-filters the data based on the criteria that I have specified:
Dim lastrow As Long
lastrow = Sheet2.Cells(Sheet2.Rows.Count, "A").End(xlUp).Row
With Sheet2
.AutoFilterMode = False
With .Range("A1:AF" & lastrow)
.AutoFilter
.AutoFilter Field:=7, Criteria1:="Yes", Operator:=xlFilterValues
End With
What I am now looking to do is delete all the Unfiltered (Hidden) rows that do not fit the criteria.
I tried so far:
Sub RemoveHiddenRows
Dim oRow As Object
For Each oRow In Sheets("Sheet2").Rows
If oRow.Hidden Then oRow.Delete
Next
End Sub
But the problem with this code is that it would only remove every other row of consecutive hidden rows because the each increments the row considered even when a row has been deleted and all lower rows have moved up one.
Also I would prefer something without a loop if it's possible, kind of like the opposite of .SpecialCells(xlCellTypeVisible).EntireRow.Delete
All help will be highly appreciated.
So I was kind of looking to get rid of Unfiltered Data rather than trying to reverse all the criteria and delete the visible cells
I would use this one:
Sub RemoveHiddenRows()
Dim oRow As Range, rng As Range
Dim myRows As Range
With Sheets("Sheet3")
Set myRows = Intersect(.Range("A:A").EntireRow, .UsedRange)
If myRows Is Nothing Then Exit Sub
End With
For Each oRow In myRows.Columns(1).Cells
If oRow.EntireRow.Hidden Then
If rng Is Nothing Then
Set rng = oRow
Else
Set rng = Union(rng, oRow)
End If
End If
Next
If Not rng Is Nothing Then rng.EntireRow.Delete
End Sub
I used Dmitry Pavliv's solution for my filtered table and it worked (thanks!) but would intermittently give error: "delete method of range class failed" error.
Error seemed to occur when only one hidden row was to be deleted. It may or may not be of significance that the lone hidden row was right under the table header.
Stepping through the code, rng pointed to correct cell, and showed just the single cell. It was probably an issue with using a Table instead of named range, though other hidden rows deleted fine in same table format.
Macro has been working fine after I modified the last portion of the code
from this:
If Not rng Is Nothing Then rng.EntireRow.Delete
To this:
If rng.Rows.Count = 1 Then
ws.Rows(rng.Row & ":" & rng.Row).Delete
ElseIf rng Is Nothing Then
rng.EntireRow.Delete
End If
For some reason, deleting that single row in this format works. I'm not quite sure why. The rng object is pointing to the correct cell and I'm using it to get the row number, so not sure why it's not working in rng.entirerow.delete statement. Oh well. Sharing as came across many posts with same error unresolved.

How to loop a dynamic range and copy select information within that range to another sheet

I have already created a VBA script that is about 160 lines long, which produces the report that you see below.
Without using cell references (because the date ranges will change each time I run this) I now need to take the users ID, name, total hours, total break, overtime 1, and overtime 2 and copy this data into sheet 2.
Any suggestions as to how I can structure a VBA script to search row B until a blank is found, when a blank is found, copy the values from column J, K, L, M on that row, and on the row above copy value C - now paste these values on sheet 2. - Continue this process until you find two consecutive blanks or the end of the data...
Even if you can suggest a different way to tackle this problem than the logic I have assumed above it would be greatly appreciated. I can share the whole code if you are interested and show you the data I began with.
Thank you in advance,
J
As discussed, here's my approach. All the details are in the code's comments so make sure you read them.
Sub GetUserNameTotals()
Dim ShTarget As Worksheet: Set ShTarget = ThisWorkbook.Sheets("Sheet1")
Dim ShPaste As Worksheet: Set ShPaste = ThisWorkbook.Sheets("Sheet2")
Dim RngTarget As Range: Set RngTarget = ShTarget.UsedRange
Dim RngTargetVisible As Range, CellRef As Range, ColRef As Range, RngNames As Range
Dim ColIDIndex As Long: ColIDIndex = Application.Match("ID", RngTarget.Rows(1), 0)
Dim LRow As Long: LRow = RngTarget.SpecialCells(xlCellTypeLastCell).Row
'Turn off AutoFilter to avoid errors.
ShTarget.AutoFilterMode = False
'Logic: Apply filter on the UserName column, selecting blanks. We then get two essential ranges.
'RngTargetVisible is the visible range of stats. ColRef is the visible first column of stats.
With RngTarget
.AutoFilter Field:=ColIDIndex, Criteria1:="=", Operator:=xlFilterValues, VisibleDropDown:=True
Set RngTargetVisible = .Range("J2:M" & LRow).SpecialCells(xlCellTypeVisible)
Set ColRef = .Range("J2:J" & LRow).SpecialCells(xlCellTypeVisible)
End With
'Logic: For each cell in the first column of stats, let's get its offset one cell above
'and 7 cells to the left. This method is not necessary. Simply assigning ColRef to Column C's
'visible cells and changing below to CellRef.Offset(-1,0) is alright. I chose this way so it's
'easier to visualize the approach. RngNames is a consolidation of the cells with ranges, which we'll
'copy first before the stats.
For Each CellRef In ColRef
If RngNames Is Nothing Then
Set RngNames = CellRef.Offset(-1, -7)
Else
Set RngNames = Union(RngNames, CellRef.Offset(-1, -7))
End If
Next CellRef
'Copy the names first, then RngTargetVisible, which are the total stats. Copying headers is up
'to you. Of course, modify as necessary.
RngNames.Copy ShPaste.Range("A1")
RngTargetVisible.Copy ShPaste.Range("B1")
End Sub
Screenshots:
Set-up:
Result:
Demo video here:
Using Filters and Visible Cells
Let us know if this helps.

How to delete row based on cell value

I have a worksheet, I need to delete rows based on cell value ..
Cells to check are in Column A ..
If cell contains "-" .. Delete Row
I can't find a way to do this .. I open a workbook, copy all contents to another workbook, then delete entire rows and columns, but there are specific rows that has to be removed based on cell value.
Need Help Here.
UPDATE
Sample of Data I have
The easiest way to do this would be to use a filter.
You can either filter for any cells in column A that don't have a "-" and copy / paste, or (my more preferred method) filter for all cells that do have a "-" and then select all and delete - Once you remove the filter, you're left with what you need.
Hope this helps.
The screenshot was very helpful - the following code will do the job (assuming data is located in column A starting A1):
Sub RemoveRows()
Dim i As Long
i = 1
Do While i <= ThisWorkbook.ActiveSheet.Range("A1").CurrentRegion.Rows.Count
If InStr(1, ThisWorkbook.ActiveSheet.Cells(i, 1).Text, "-", vbTextCompare) > 0 Then
ThisWorkbook.ActiveSheet.Cells(i, 1).EntireRow.Delete
Else
i = i + 1
End If
Loop
End Sub
Sample file is shared: https://www.dropbox.com/s/2vhq6vw7ov7ssya/RemoweDashRows.xlsm
You could copy down a formula like the following in a new column...
=IF(ISNUMBER(FIND("-",A1)),1,0)
... then sort on that column, highlight all the rows where the value is 1 and delete them.
if you want to delete rows based on some specific cell value.
let suppose we have a file containing 10000 rows, and a fields having value of NULL.
and based on that null value want to delete all those rows and records.
here are some simple tip.
First open up Find Replace dialog, and on Replace tab, make all those cell containing NULL values with Blank.
then press F5 and select the Blank option, now right click on the active sheet, and select delete, then option for Entire row.
it will delete all those rows based on cell value of containing word NULL.
If you're file isn't too big you can always sort by the column that has the - and once they're all together just highlight and delete. Then re-sort back to what you want.
You can loop through each the cells in your range and use the InStr function to check if a cell contains a string, in your case; a hyphen.
Sub DeleteRowsWithHyphen()
Dim rng As Range
For Each rng In Range("A2:A10") 'Range of values to loop through
If InStr(1, rng.Value, "-") > 0 Then 'InStr returns an integer of the position, if above 0 - It contains the string
rng.Delete
End If
Next rng
End Sub
This is the autofilter macro you could base a function off of:
Selection.AutoFilter
ActiveSheet.Range("$A$1:$A$10").AutoFilter Field:=1, Criteria1:="=*-*", Operator:=xlAnd
Selection.AutoFilter
I use this autofilter function to delete matching rows:
Public Sub FindDelete(sCol As String, vSearch As Variant)
'Simple find and Delete
Dim lLastRow As Integer
Dim rng As Range
Dim rngDelete As Range
Range(sCol & 1).Select
[2:2].Insert
Range(sCol & 2) = "temp"
With ActiveSheet
.usedrange
lLastRow = .Cells.SpecialCells(xlCellTypeLastCell).Row
Set rng = Range(sCol & 2, Cells(lLastRow, sCol))
rng.AutoFilter Field:=1, Criteria1:=vSearch, Operator:=xlAnd
Set rngDelete = rng.SpecialCells(xlCellTypeVisible)
rng.AutoFilter
rngDelete.EntireRow.Delete
.usedrange
End With
End Sub
call it like:
call FindDelete "A", "=*-*"
It's saved me a lot of work. Good luck!