Handling Merged Cells when Deleting Rows - vba

I'm working on writing a module to remove unwanted text from a number of worksheets within a single workbook. I've pieced together enough to remove rows that have a specific font type, and rows that are empty; however, I've hit a snag.
The worksheets have a number of merged cells. I want to delete specific rows based on key phrases. Example, if "Comments" is found anywhere in Column A delete the row. However, if comments is merged between A2:A4, text in B3:B4 remains, leaving junk in the sheets I don't want.
Is there a way to delete the merged cell, and all rows to the right of that cell, if in the value in Column A is any number of keywords I'm looking for?
Here's what I have so far...
Sub Delete_Rows_Courier()
Dim ws As Excel.Worksheet
Dim LastRow As Long
Dim i As Integer
For Each ws In Application.ThisWorkbook.Worksheets
LastRow = ws.Cells(Rows.Count, 1).End(xlUp).Row
i = 1
Do While i <= LastRow
If ws.Range("A" & i).Font.Name = "Courier New" Then
ws.Rows(i).Delete
i = i - 1
LastRow = LastRow - 1
End If
i = i + 1
Loop
Next
End Sub
Sub Delete_Empty_Rows()
Dim ws As Worksheet
Dim wb As Workbook
Dim i As Long
For Each ws In Application.ThisWorkbook.Worksheets
'Deletes the entire row within the selection if the ENTIRE row contains no data.
'We use Long in case they have over 32,767 rows selected.
'We turn off calculation and screenupdating to speed up the macro.
With Application
.Calculation = xlCalculationManual
.ScreenUpdating = False
'We work backwards because we are deleting rows.
For i = ws.UsedRange.Rows.Count To 1 Step -1
If WorksheetFunction.CountA(ws.Rows(i)) = 0 Then
ws.Rows(i).EntireRow.Delete
End If
Next i
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
Next ws
End Sub
Sub RunMacros()
Delete_Empty_Rows
Delete_Rows_Courier
End Sub

With .Range("A" & i).Mergearea
x = .Rows.Count 'if you need to know how many rows were deleted
.EntireRow.Delete 'delete merged rows
End With

Related

Excel VBA - Find matching column headings and delete the column

Apologies if this has been answered before, I'm unable to find anything that matches my specific case.
I have a workbook with 18 sheets, and a variable number of columns per sheet starting at B2. Occasionally the program that generates the sheet will create duplicate columns, due to this, I need a macro triggered by button to search each sheet for matching column headers and then delete one of these columns (the whole column, not just the header).
So far I'm pretty stuck, I've been able to delete all matches from any cell in the sheet, which pretty much wipes the entire sheet out. I just need to match headers and then delete the entire column based on that.
Let me know if you need any more information, and thank you for the help!
What I have so far, the code is doing some other stuff too so this needs to continue working.
Sub RemoveExtras()
Dim MyRange As Range
Dim ws As Worksheet
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
BadCharacters = Array(Chr(10), Chr(13))
wsNumber = Sheets.Count
For Each ws In Worksheets
With ws
For Each MyRange In .UsedRange
If 0 < InStr(MyRange, Chr(10)) Then
For Each i In BadCharacters
MyRange = Replace(MyRange, i, vbNullString)
Next i
End If
For t = 1 To wsNumber
Columns(t).RemoveDuplicates Columns:=Array(1), Header:=xlYes
Next t
Next MyRange
End With
Next ws
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
Dictionaries are perfect for handling unique values:
Sub RemoveExtras()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim c As Integer, i As Integer, ws As Worksheet
Dim dict As Object
For Each ws In Worksheets
Set dict = CreateObject("Scripting.Dictionary")
'Find Last column
c = ws.UsedRange.Columns.Count
'Loop backwards
For i = c To 2 Step -1
'If column does not exist in dictionary, then add it
If Not dict.Exists(ws.Cells(2, i).Value) Then
dict.Add ws.Cells(2, i).Value, 1
Else
'Otherwise delete column
ws.Columns(i).Delete Shift:=xlToLeft
End If
Next i
Set dict = Nothing
Next
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
Here you do not compare every pair of column headers in the sheet. Also this compares headers across all the worksheets, not just duplicates inside one individual sheet.
See if this helps you
Sub test()
Dim book As Workbook, sheet As Worksheet, text As String
For Each sheet In Worksheets
Set MR = Range("B2:Z2") 'change Z2 as per your requirement
For Each cell In MR
Set BR = Range("B2:Z2") 'change Z2 as per your requirement
For Each cell2 In BR
If cell.Value = cell2.Value Then cell.EntireColumn.Delete
Next
Next
Next sheet
End Sub

Remove rows in excel

Here is a tricky question.
I have an Excel file containing roughly 4000 articles in each of the 10 sheets within the workbook. I would like to keep only about 400 articles and the remaining 3600 articles should be removed.
All sheets within the workbook has the article ID in column A.
All sheets has headers on row 1-5.
The article ID can exist more than once in some of the sheets.
I want to list the 400 article ID's in the Visual Basic Script itself so that i don't have to create a separate sheet or column that contains the information.
Can someone please help me? I have tried so many scripts, but nothing seems to work...
In the example below I want to keep Article ID's 5 and 1 (and of course the headers). The remaining 5 rows should be removed
This is what i have tried:
Sub Delete()
Dim Firstrow As Long
Dim Lastrow As Long
Dim Lrow As Long
Dim CalcMode As Long
Dim ViewMode As Long
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
End With
With ActiveSheet
.Select
ViewMode = ActiveWindow.View
ActiveWindow.View = xlNormalView
.DisplayPageBreaks = False
Firstrow = 6
Lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
For Lrow = Lastrow To Firstrow Step -1
With .Cells(Lrow, "Y")
If Not IsError(.Value) Then
If InStr(.Value, 1) = 0 Or InStr(.Value, 5) = 0 Then .EntireRow.Delete
End If
End With
Next Lrow
End With
End Sub
But, I get two issues:
All rows are removed including the rows that I want to remove (1 and 5).
It only works on the open sheet and not the whole workbook.
Kind regards,
Peter
Try this code. At the start it will ask what IDs you want to keep in all worksheets. There you enter numbers separated by comma (,), no spaces or character other than comma and digits aren't allowed.
Sub DeleteArticles()
Dim i As Long
Dim strIDToKeep As String
Dim arrIDToKeep() As String
Dim ws As Worksheet
Dim lastRow As Long
strIDToKeep = InputBox("What IDs to keep?")
arrIDToKeep = Split(strIDToKeep, ",")
For Each ws In Worksheets
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
For i = lastRow To 6 Step -1
'if ID isn't present in array of IDs to keep, then we delete entire row
If UBound(Filter(arrIDToKeep, ws.Cells(i, 1).Value)) = -1 Then
ws.Rows(i).EntireRow.Delete
End If
Next
Next
End Sub
Try this code
Sub Test()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
Dim i As Long
For i = 10 To 2 Step -1
Select Case ws.Cells(i, 1).Value
'add your ids for which you don't want to delete the rows
Case 1, 5
'do nothing
Case Else
ws.Cells(i, 1).EntireRow.Delete
End Select
Next i
Next ws
End Sub

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

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

Large range duplicate removal from another sheet

The object is to remove all the rows in sheet1 column A if they exist in the list in sheet2 column A.
Both columns only contain numbers.
Sheet one column A may contain duplicates which is fine if they are not on the list in sheet2.
One option that I'm not familiar with and might be missing out on is Autofilter.
The code executes on a small data range 100 to 1000 but I have many books with over 1,000,000 records to clean up and anything over 10,000 brings Excel to not responding and freezes up indefinitely.
Sub remDupesfromTwoWs()
With Application
.EnableEvents = False
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
End With
' set range to be searched
Dim masterRecordRange As Range ' declare an unallocated array.
Set masterRecordRange = Range("Sheet1!A2:A316730") ' masterRecordRange is now an allocated array
' store sheet2 column A as searchfor array
Dim unwantedRecords() As Variant ' declare an unallocated array.
unwantedRecords = Range("Sheet2!A1:A282393") ' unwantedRecords is now an allocated array
' foreach masterRecord loop to search masterRecordRange for match in unwantedRecords
Dim i As Double
Dim delRange As Range
Set delRange = Range("A" & ActiveSheet.Rows.Count)
'go through all rows starting at last row
For i = masterRecordRange.Rows.Count To 1 Step -1
' loop through unwantedRecords check each offset
For Each findMe In unwantedRecords
'If StrComp(cell, findMe, 1) = 0 Then not as fast
' unwantedRecord found
If Cells(i, 1).Value = findMe Then
Set delRange = Union(delRange, Range("A" & i))
'MsgBox i
Exit For
End If
Next findMe
Next i
'remove them all in one shot
delRange.EntireRow.Delete
With Application
.EnableEvents = True
CalcMode = .Calculation
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
'possibly count and display quantity found
MsgBox "finally done!"
End Sub
It is very slow to walk through a range one cell at a time because there is a large overhead on each call to Cells. So you should get both ranges into variant arrays, then compare them to build up another array of matches which you would then write back to the worksheet and use Autofilter to select the rows to delete.
Here is a blog post on various methods of comparing lists:
VBA Comparing lists shootout
The fastest method is to use either a Dictionary or a collection. You should be able to adapt the code to do what you want.
Have you ever tried Range.Find:
Sub TestIt()
Dim ws1 As Worksheet, ws2 As Worksheet
Dim LastRow As Long, DestLast As Long, CurRow As Long
Set ws1 = Sheets("Sheet1")
Set ws2 = Sheets("Sheet2")
LastRow = ws1.Range("A" & Rows.Count).End(xlUp).Row
DestLast = ws2.Range("A" & Rows.Count).End(xlUp).Row
For CurRow = LastRow to 2 Step -1 'Must go backwards because you are deleting rows
If Not ws2.Range("A2:A" & DestLast).Find(ws1.Range("A" & CurRow).Value, LookIn:=xlValues, LookAt:=xlWhole) is Nothing Then
Range("A" & CurRow).EntireRow.Delete xlShiftUp
End If
Next CurRow
End Sub

Store AutoFilter Row Numbers using VBA

How do I store and retrieve the row numbers returned from an AutoFilter action using VBA? For example, I used #brettdj code from this question (see code below) to delete all rows with "X" under column B. Now I need to store the row numbers with X (B4,B6,B9 - see screen shots below) because I need to delete the same rows on other sheets in the same workbook.
Sub QuickCull()
Dim ws As Worksheet
Dim rng1 As Range
Set ws = Sheets("Sheet1")
Set rng1 = ws.Range(ws.[b2], ws.Cells(Rows.Count, "B").End(xlUp))
Application.ScreenUpdating = False
With ActiveSheet
.AutoFilterMode = False
rng1.AutoFilter Field:=1, Criteria1:="X"
rng1.Offset(1, 0).EntireRow.Delete
.AutoFilterMode = False
End With
Application.ScreenUpdating = True
End Sub
Using the code from Is it possible to fill an array with row numbers which match a certain criteria without looping? you could return these rows quickly without the AutoFilter
For example, this code will return a range of rows where X is found within B2:B50000
Sub GetEm()
Dim StrRng As String
StrRng = Join(Filter(Application.Transpose(Application.Evaluate("=IF(B2:B50000=""X"",""B""&ROW(B2:B50000),""X"")")), "X", False), ",")
If Len(StrRng) > 0 Then MsgBox Range(StrRng).EntireRow.Address & " could be deleted elsewhere"
End Sub