Macro: Removing group of rows associated with cell in first column based on criteria, then deleting blank rows - vba

I'm having a difficult time merging code to accomplish my goal. I am working between two sheets within one workbook. Column "A" references an item that may have multiple rows in column "C". "C" could have thousands of label codes, but there are 52 label codes that are listed in sheet "SheetCode". What my goal is to look at an item and see if it has one of the 52 label codes and if so then remove the item and all rows below it until the next Item in Column "A" label number. I want my macro to:
Search Column C for any value listed in sheet "SheetCode" (A2:A53)
If found, reference the associated populated cell in Column A and delete all rows below until it runs into the next populated cell in Column A, but continues to search the rest of column "C" for more (A2:A53) values.
Loop
I posted 2 images. The SheetCode worksheet has the list of values. I adde conditional formatting so that any cell value in main spreadsheet is colored. Ultimately the code should then delete all rows below the Column A value. This example would show rows 14-21 and 29-44 deleted.
Here is what I have so far. My problem is I want to avoid
Sub Remove_TBI_AB()
Const TEST_COLUMN As String = "C"
Dim Lastrow As Long
Dim EndRow As Long
Dim i As Long
Application.ScreenUpdating = False
With ActiveSheet
Lastrow = .Cells(.Rows.Count, TEST_COLUMN).End(xlUp).Row
EndRow = Lastrow
For i = Lastrow To 1 Step -1
If .Cells(i, TEST_COLUMN).Value2 Like "161000" Then
'Here I could at continuous "_or" and then in next line add the next code to find, but I have the list and would rather reference the list of values
.Rows(i & ":" & EndRow).Delete
EndRow = i - 1
' Here I need code to delete all cells below the associated value in Column A until the next populated cell.
EndRow = i - 1
End If
Next i
End With
Application.ScreenUpdating = True
End Sub
SheetCode; values to target
Main Worksheet

You're on the right track, with some use of arrays and worksheet functions it could be completed; the key is that we will iterate backward "by item zone" instead of by individual rows. For each item zone, if at least one code is matched in the SheetCode list, the whole zone is deleted.
Sub Remove_TBI_AB()
Application.ScreenUpdating = False: Application.Calculation = xlCalculationManual
On Error GoTo Cleanup
Dim codes As Range: Set codes = Worksheets("Sheetcode").Range("A2:A53")
Dim lastrow As Long, startRow As Long
'[startRow, lastRow] mark the start/end of current item
With Worksheets("Main")
lastrow = .Cells(.Rows.count, 3).End(xlUp).row
Do While lastrow > 1
startRow = lastrow
Do Until Len(Trim(.Cells(startRow, 1).Value2)) > 0
startRow = startRow - 1
Loop ' find the beginning of current item
With .Range("C" & startRow & ":C" & lastrow) ' range of current item codes
If Application.SumProduct(Application.CountIf(codes, .Value2)) > 0 Then
.EntireRow.Delete ' at least one code was matched
End If
End With
lastrow = startRow - 1
Loop ' restart with next item above
End With
Cleanup:
Application.ScreenUpdating = False: Application.Calculation = xlCalculationAutomatic
End Sub

Related

VBA Script which compares two sheets and returns unique values in a new sheet

I created a VBA Script which compares a sheet named "New Data" and a sheet named "Old Data" to return only rows with a column differing. I created this in order to find back dating which occurs from time to time, so the two sheets "New Data" and "Old Data" should be identical with 1-5 differences, sometimes they add a new row, sometimes they take one of the old data and change the value attached to it. This is why I have it checking as unique rows, its fine if column a-y is identical but once column z differs then its a unique row.
Please see code below:
Sub CompareSheets()
'Declare variables for the two sheets
Dim sheet1 As Worksheet
Dim sheet2 As Worksheet
'Set the variables to the appropriate sheets
Set sheet1 = ThisWorkbook.Sheets("New Data")
Set sheet2 = ThisWorkbook.Sheets("Old Data")
'Declare a variable for the last row in each sheet
Dim lastRow1 As Long
Dim lastRow2 As Long
'Set the last row variables to the last used row in each sheet
lastRow1 = sheet1.Cells(sheet1.Rows.Count, "A").End(xlUp).Row
lastRow2 = sheet2.Cells(sheet2.Rows.Count, "A").End(xlUp).Row
'Declare a variable for the first empty row in a new sheet
Dim nextRow As Long
'Create a new sheet to hold the unique rows
ThisWorkbook.Sheets.Add After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)
ActiveSheet.Name = "Unique Rows"
'Set the first empty row variable to the first row of the new sheet
nextRow = 1
' Create a dictionary object
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
' Add the data from sheet2 to the dictionary
Dim key As String
For row2 = 2 To lastRow2
key = ""
For col = 1 To sheet2.Cells(1, sheet2.Columns.Count).End(xlToLeft).Column
key = key & sheet2.Cells(row2, col)
Next col
dict.Add key, row2
Next row2
' Turn off screen updating and calculation during execution
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'Loop through each row in sheet1
For row1 = 2 To lastRow1
key = ""
'Create a key for the current row
For col = 1 To sheet1.Cells(1, sheet1.Columns.Count).End(xlToLeft).Column
key = key & sheet1.Cells(row1, col)
Next col
'Check if the key exists in the dictionary
If Not dict.Exists(key) Then
For col = 1 To sheet1.Cells(1, sheet1.Columns.Count).End(xlToLeft).Column
ThisWorkbook.Sheets("Unique Rows").Cells(nextRow, col).Value = sheet1.Cells(row1, col).Value
Next col
nextRow = nextRow + 1
End If
Next row1
'Turn on screen updating and calculation
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
I've had this script work and give me unique variables from time to time but in some cases it gives me an error: Run-Time Error '457': That key is already associated with an element of this collection. I've received one other error Run-Time Error '1004' but that's only when Unique Rows sheet already exists, if I delete it would fix this issue. Can you please assist on having this code run smoothly without error?
Thank you, I appreciate your time and effort!

Excel Delete empty row in a looped range [duplicate]

This macro is designed to compare the data in column C and D and if C does not match D in a certain row, it deletes the entire tow. The problem is that it deletes the headers in Row 1 on the Excel sheet because they don't match. How do I run the macro for rows 2 through 9999 instead of all 9999 rows.
Sub deleteNonMatchingRows()
Dim i As Long
For i = 9999 To 1 Step -1 ' it will scan 9999 rows of the sheet. This number can be increased for larger sheets
If Range("C" & i) <> Range("D" & i) Then
Range("C" & i).EntireRow.Delete
End If
Next
End Sub
If you use a descriptive variable naming, eg. rename i into iRow you will never forget that this is your row counter, that is counting from row 9999 to row 1 in For iRow = 9999 To 1 Step -1. So you need to change the 1 into a 2 to omit the first row.
I recommend to use a dynamic start for your loop that automatically finds the last used row. This prevents unnecessary loop steps and you don't need to increase it for larger worksheets.
Option Explicit
Public Sub DeleteNonMatchingRows()
Dim LastRow As Long
LastRow = Cells(Rows.Count, "C").End(xlUp).Row 'find last used row in column C
Dim iRow As Long
For iRow = LastRow To 2 Step -1
If Range("C" & iRow) <> Range("D" & iRow) Then
'Range("C" & iRow).EntireRow.Delete
Rows(iRow).Delete 'directy delete a row
End If
Next iRow
End Sub
Deletion of a row is an operation that takes quite some time. Thus, it is a good idea to make all deletions at once, uniting all rows to be deleted in a specific range wholeRange:
Option Explicit
Public Sub DeleteNonMatchingRows()
Dim LastRow As Long
LastRow = Cells(Rows.Count, "C").End(xlUp).Row
Dim wholeRange As Range
Dim iRow As Long
For iRow = LastRow To 2 Step -1
If Range("C" & iRow) <> Range("D" & iRow) Then
If wholeRange Is Nothing Then
Set wholeRange = Rows(iRow)
Else
Set wholeRange = Union(wholeRange, Rows(iRow))
End If
End If
Next iRow
If Not wholeRange Is Nothing Then
wholeRange.Select 'delete this row
Stop 'delete this row
wholeRange.Delete
End If
End Sub
Once you run the code, it will stop on the Stop line. You will be able to see the range, which is to be deleted. The range will be selected. Once you see it, it is a good idea to delete the two rows, mentioned in the comments, you are not going to need them any more.
you can avoid loops:
Sub deleteNonMatchingRows()
With Range("C2", Cells(Rows.Count, "C").End(xlUp)) ' reference column C cells from row 2 doen to last not empty one
With .Offset(, .Parent.UsedRange.Columns.Count) ' reference referenced range offset by active sheet used range columnns (to be sure you'r not overwriting already filled cells)
.FormulaR1C1 = "=IF(RC3<>RC4,1,"""")" ' have referenced cells show a "1" if corresponding cells in column C and D match
.SpecialCells(xlCellTypeFormulas, xlNumbers).EntireRow.Delete ' delete all rows whose referenced column formula result is a number
.ClearContents ' clear referenced range
End With
End With
End Sub

How to Copy data from one sheet to another based on cell value

I have a master dataset in one sheet something like this
In another sheet I want to extract Data from this Master Data set based on a cell value which will have Department as Criteria.
So ,For example if in sheet 2 I type my my Department criteria as Sales, All the rows with Department as Sales should get populated in Sheet 2.
Can this be done by excel functions/Macros/VBA ?
Thanks in advance.
Pardon me if you find this query absurd .
Yes it can be done. Add this code to a module in your workbook. You can run it right from the debugger, or go to macros and assign it a hotkey, or you can create a button on the sheet and put this code inside the button click event. There are probably more efficient ways to do this but it shouldn't matter unless you have a lot of data. I've been explicit so you can see whats going on. I've used the sheet names "Data" and "View" you will have to change the names in the code to match your workbook.
EDIT: I'm also assuming that the top left cell in your screenshot is A1, if not you will have to adjust the code.
Sub GetDepartments()
Dim LastRow As Long
Dim MyRange As Range
Dim SourceRow As Long
Dim DeptValue As String
Dim OutputRow As Long
Dim ColCounter As Long
'get size of current view sheet and clear
LastRow = ThisWorkbook.Worksheets("View").Cells(ThisWorkbook.Worksheets("View").Rows.Count, "A").End(xlUp).Row
If LastRow > 7 Then
Set MyRange = ThisWorkbook.Worksheets("View").Range("A7:C" & LastRow)
MyRange.ClearContents
End If
'get size of data sheet
LastRow = ThisWorkbook.Worksheets("Data").Cells(ThisWorkbook.Worksheets("Data").Rows.Count, "A").End(xlUp).Row
'get value to match
DeptValue = ThisWorkbook.Worksheets("View").Cells(3, 2).Value
'track outputrow
OutputRow = 7
'loop through all rows in data
For SourceRow = 2 To LastRow
'if match found
If ThisWorkbook.Worksheets("Data").Cells(SourceRow, 2).Value = DeptValue Then
'copy 3 cols
For ColCounter = 1 To 3
ThisWorkbook.Worksheets("View").Cells(OutputRow, ColCounter).Value = _
ThisWorkbook.Worksheets("Data").Cells(SourceRow, ColCounter).Value
Next ColCounter
'increment outputrow
OutputRow = OutputRow + 1
End If
Next SourceRow
End Sub

Infinite loop while gathering datasets from several worksheets

This is my first time to code in VBA.
I have several worksheets in a file and they are in order by dates.
So what I am trying to do is to collect data sets in a worksheet if they have the same period of time.
date1 value1
date2 value2
date3 value3
Since they are in order I just compare the first date values and if they are different it moves on to the next worksheet. If they are the same then copy the value and do the same process until it reaches the last worksheet.
However it copies one worksheet fine but after that Excel freezes.
I would be appreciated if you find any errors or give me other suggestions to do it.
Following is my code:
Sub matchingStock()
Dim sh1 As Worksheet, sh2 As Worksheet
' create short references to sheets
' inside the Sheets() use either the tab number or name
Set sh1 = Sheets("combined")
Dim col As Long
'since first column is for Tbill it stock price should place from the third column
col = 3
Dim k As Long
'go through all the stock worksheets
For k = Sheets("WLT").Index To Sheets("ARNA").Index
Set sh2 = Sheets(k)
' Create iterators
Dim i As Long, j As Long
' Create last rows values for the columns you will be comparing
Dim lr1 As Long, lr2 As Long
' create a reference variable to the next available row
Dim nxtRow As Long
' Create ranges to easily reference data
Dim rng1 As Range, rng2 As Range
' Assign values to variables
lr1 = sh1.Range("A" & Rows.Count).End(xlUp).Row
lr2 = sh2.Range("A" & Rows.Count).End(xlUp).Row
If sh1.Range("A3").Value = sh2.Range("A3").Value Then
Application.ScreenUpdating = False
' Loop through column A on sheet1
For i = 2 To lr1
Set rng1 = sh1.Range("A" & i)
' Loop through column A on sheet1
For j = 2 To lr2
Set rng2 = sh2.Range("A" & j)
' compare the words in column a on sheet1 with the words in column on sheet2
'Dim date1 As Date
'Dim date2 As Date
'date1 = TimeValue(sh1.Range("A3"))
'date2 = TimeValue(sh2.Range("A3"))
sh1.Cells(1, col).Value = sh2.Range("A1").Value
' find next empty row
nxtRow = sh1.Cells(Rows.Count, col).End(xlUp).Row + 1
' copy the word in column A on sheet2 to the next available row in sheet1
' copy the value ( offset(0,1) Column B ) to the next available row in sheet1
sh1.Cells(nxtRow, col).Value = rng2.Offset(0, 6).Value
'when the date is different skip to the next worksheet
Set rng2 = Nothing
Next j
Set rng1 = Nothing
Next i
'sh3.Rows("1:1").Delete
Else
GoTo Skip
End If
Skip:
col = col + 1
Next k
End Sub
I cannot identify a specific error so this is a list of suggestions that may help you identify the error and may help improve your code.
Suggestion 1
Do you think the Else block of If-Then-Else-End-If is compulsory?
If sh1.Range("A3").Value = sh2.Range("A3").Value Then
:
Else
GoTo Skip
End If
Skip:
is the same as:
If sh1.Range("A3").Value = sh2.Range("A3").Value Then
:
End If
Suggestion 2
I do not like:
For k = Sheets("WLT").Index To Sheets("ARNA").Index
The value of property Index for a worksheet may not what you think it is. This may not give you the set or sequence of worksheets you expect. Do you want every worksheet except "Combined"? The following should be more reliable:
For k = 1 To Worksheets.Count
If Worksheets(k).Name <> sh1.Name Then
:
End If
Next
Suggestion 3
You use:
.Range("A" & Rows.Count)
.Range("A3")
.Cells(1, col).Value
.Cells(Rows.Count, col)
rng2.Offset(0, 6)
All these methods of identifying a cell or a range have their purposes. However, I find it confusing to use more than one at a time. I find .Cells(row, column) and .Range(.Cells(row1, column1), .Cells(row2, column2)) to be the most versatile and use them unless there is a powerful reason to use one of the other methods.
Suggestion 4
I cannot decypher what this code is attempting to achieve.
You say: "I have several worksheets in a file and they are in order by dates. So what I am trying to do is to collect data sets in a worksheet if they have the same period of time."
If you have set Worksheet("combined").Range("A3").Value to a particular date and you want to collect data from all those sheets with the same value in cell A3 then the outer For-Loop and the If give this effect. But if so, if does not matter how the worksheets are ordered. Also you start checking cell values from row 2 which suggests row 3 is a regular data row.
The outer loop is for each worksheet, the next loop is for each row in "combined" and the inner loop is for each row in the worksheet selected by the outer loop. The middle loop does not appear to do anything but set rng1 which is not used.
Perhaps you can add an explanation of what you are trying to achieve.
Suggestion 5
Are you trying to add an entire column of values from the source worksheets to "Combined". The macro below:
Identifies the next free row in column A of "Combined"
Identifies the last used row in column A of "Sheet2"
Assumes the first interesting row of "Sheet2" is 2.
Adds the entire used range of column A of "Sheet2" (complete with formatting) to the end of "Combined"'s column A in a single statement.
This may demonstrate a better way of achieving the effect you seek.
Sub Test()
Dim RngSrc As Range
Dim RngDest As Range
Dim RowCombNext As Long
Dim RowSrcFirst As Long
Dim RowSrcLast As Long
With Worksheets("Combined")
RowCombNext = .Cells(Rows.Count, "A").End(xlUp).Row + 1
Set RngDest = .Cells(RowCombNext, "A")
End With
With Worksheets("Sheet2")
RowSrcFirst = 2
RowSrcLast = .Cells(Rows.Count, "A").End(xlUp).Row
Set RngSrc = .Range(.Cells(RowSrcFirst, "A"), .Cells(RowSrcLast, "A"))
End With
RngSrc.Copy Destination:=RngDest
End Sub

Copy cells from specified column, removing duplicates

I'm newbie in VBA, what I need to do is to copy rows from specified column into a column on the other worksheet, but I want to copy just one occurance of each word, for example
Column "F"
dog
dog
cat
dog
In the result I need to have new Worksheet called "Animals" with:
Column "A" Column "B"
1 dog
2 cat
Here is a sub routine that will do exactly what you want: slap a list of unique elements in Sheet1 column F into column A of sheet2 and rename the sheet "animals". You could tweak this so that instead of it changing the name of sheet2 it can create a new sheet if you like.
Sub UniqueList()
Application.ScreenUpdating = False
Dim lastRow As Long
Dim i As Long
Dim dictionary As Object
Set dictionary = CreateObject("scripting.dictionary")
Sheet1.Activate
lastRow = Sheet1.Cells(Rows.count, "F").End(xlUp).row
On Error Resume Next
For i = 1 To lastRow
If Len(cells(i, "F")) <> 0 Then
dictionary.Add cells(i, "F").Value, 1
End If
Next
Sheet2.range("a1").Resize(dictionary.count).Value = _
Application.Transpose(dictionary.keys)
Application.ScreenUpdating = True
MsgBox dictionary.count & " unique cell(s) were found and copied."
End Sub
How it works: I use a dictionary file, which will automatically take out any dupes, then slap the list of entries into sheet2.
Do you need to do this in VBA at all?
If you just want to get a unique copy of your list, select the unsorted, non-unique column contents including the header, then hit the Advanced... button on the Sort and Filter pane of the Data ribbon. You can ask it to copy to another location and tick Unique records only.
Recording this activity and looking at the VBA, this is how it looks:
Range("A1:A4").AdvancedFilter Action:=xlFilterCopy, CopyToRange:=Range("B1"), Unique:=True
here is a solution:
Option Explicit
Sub copyNoDuplicates()
Dim rLastCell As Range
Dim cell As Range, i As Long
Dim cAnimals As Collection
Set cAnimals = New Collection
With ActiveWorkbook.Worksheets("Sheet1")
'Find last used cell
Set rLastCell = .Range("F65536").End(xlUp)
'Parse every animal and put it in a collection
On Error Resume Next
For Each cell In .Range("F2:F" & rLastCell.Row)
cAnimals.Add cell.Value, CStr(cell.Value)
Next cell
On Error GoTo 0
End With
With ActiveWorkbook.Worksheets("Sheet2")
For i = 1 To cAnimals.Count
.Range("A" & i).Value = i
.Range("B" & i).Value = cAnimals(i)
Next i
End With
End Sub