Excel VBA Remove content from cells then delete total rows - vba

I am programming to review a range, remove content from the range of cells, then delete the entire row of cells that have been cleared of content. Currently, I run the code and all the rows are getting deleted. Also, I appreciate suggestions to make the code DRY.
Option Explicit
Sub Alfredo()
Dim msg As String
Dim VarCase As Range
Dim ws As Worksheets
Set ws = Sheets("Data")
For Each VarCase In ws.Range("D1:D11000")
If VarCase.Value2 = "John" Or VarCase.Value2 = "Thompson" Or VarCase.Value2 =
"Mattson" Then
VarCase.ClearContents
End If
Next VarCase
For Each VarCase In ws.Range("D1:D11000")
If VarCase.Value = "" Then
Rows.EntireRow.Delete
End If
Next VarCase
End Sub

In your final For loop, you have
Rows.EntireRow.Delete
where you should have
VarCase.EntireRow.Delete
which probably accounts for the general deletion.
The For Each construct doesn't always work happily with a range that is being changed (here by row deletion), so beware that. You could potentially accumulate a Range of deletion targets via Union and delete in one statement at the end for DRYness, without any clearing of contents.
Also, indentation is your friend.
Edit to add illustration of Union approach:
Sub TestRowDelete()
Dim ARange As Range
Dim DRange As Range
Set DRange = Nothing
For Each ARange In ActiveSheet.UsedRange.Rows
If ARange(1).Value = "d" Then ' testing first cell on each row
If DRange Is Nothing Then
Set DRange = ARange
Else
Set DRange = Union(DRange, ARange)
End If
End If
Next ARange
If Not DRange Is Nothing Then DRange.EntireRow.Delete
End Sub

Just put this code under this code under the data worksheet in VBA
Sub Alfredo()
Dim msg As String
Dim VarCase As Range
For Each VarCase In ActiveSheet.Range("D:D")
If VarCase.Value2 = "John" Or VarCase.Value2 = "Thompson" Or VarCase.Value2 = "Mattson" Then
VarCase.ClearContents
End If
Next VarCase
For Each VarCase In ActiveSheet.Range("D:D")
If VarCase.Value = "" Then
VarCase.EntireRow.Delete
End If
Next VarCase
End Sub

Why loop twice?
Application.ScreenUpdating = False
Dim VarCase As Range
Dim ws As Worksheet
Set ws = Sheets("Data")
For Each VarCase In ws.Range("D1:D11000")
If VarCase.Value2 = "John" Or VarCase.Value2 = "Thompson" Or VarCase.Value2 = "Mattson" Then
VarCase.ClearContents
VarCase.EntireRow.Delete
End If
Next VarCase

Related

VBA to duplicate large dataset using array

I have data on sheet A and want to duplicate it on sheet B. Because it is a lot of data, I do not want to use copy-paste. If I really simplify it, this is my code. My ranges change although I made it sort of fixed in this simplified code. I do not want to use something like range("A1:BBB100000") since my range will change. I get 1004 error "Application-defined or object-defined error". What am I doing wrong?
Dim origin(1 to 100000, 1 to 100000) as Variant
Dim dest(1 to 100000, 1 to 100000) as Variant
Set A=Worksheets("A")
Set B=Worksheets("B")
Vrow=100000
set origin=A.range(cells(1,1),cells(Vrow, Vrow))
set dest=B.range(cells(1,1),cells(Vrow, Vrow))
dest=origin
You don't need the array. Only generate an array if your actually going to do any calculations on it. If you just want to do value -> value then that's what you do (as shown below).
Remember to always declare all your variables as well.
Dim A As Worksheet, B As Worksheet, Vrow As Long
Set A = Worksheets("A")
Set B = Worksheets("B")
Vrow = 100000
B.Range(B.Cells(1, 1), B.Cells(Vrow, Vrow)).Value = A.Range(A.Cells(1, 1), A.Cells(Vrow, Vrow)).Value
Copy Range Values to Another Worksheet
Sub CopyValues()
Const sName As String = "A"
Const sFirstCellAddress As String = "A1"
Const dName As String = "B"
Const dFirstCellAddress As String = "A1"
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim sfCell As Range: Set sfCell = sws.Range(sFirstCellAddress)
Dim srg As Range: Set srg = sfCell.CurrentRegion
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dfCell As Range: Set dfCell = dws.Range(dFirstCellAddress)
Dim drg As Range: Set drg = dfCell.Resize(srg.Rows.Count, srg.Columns.Count)
drg.Value = srg.Value
End Sub
Sub CopyValuesShorter()
Dim srg As Range
Set srg = ThisWorkbook.Worksheets("A").Range("A1").CurrentRegion
Dim drg As Range
Set drg = ThisWorkbook.Worksheets("B").Range("A1") _
.Resize(srg.Rows.Count, srg.Columns.Count)
drg.Value = srg.Value
End Sub
Sub CopyValuesShortest()
With ThisWorkbook.Worksheets("A").Range("A1").CurrentRegion
ThisWorkbook.Worksheets("B").Range("A1") _
.Resize(.Rows.Count, .Columns.Count).Value = .Value
End With
End Sub

Split Worksheets

Currently this macro splits worksheets based on a cell.
It works well, however I am putting it as a button on a different page but this selects the active page, I want it to run this macro on a specific sheet.
Sub SplitToWorksheets_step4()
'Splits the workbook into different tabs
Dim ColHead As String
Dim ColHeadCell As Range
Dim icol As Integer
Dim iRow As Long 'row index on Fan Data sheet
Dim Lrow As Integer 'row index on individual destination sheet
Dim Dsheet As Worksheet 'destination worksheet
Dim Fsheet As Worksheet 'fan data worksheet (assumed active)
Again:
'ColHead = Worksheets("Diversion Report") 'this ask the user to enter a colunm name
ColHead = InputBox("Enter Column Heading", "Identify Column", [c1].Value) 'this ask the user to enter a colunm name
If ColHead = "" Then Exit Sub
Set ColHeadCell = Rows(1).Find(ColHead, LookAt:=xlWhole)
If ColHeadCell Is Nothing Then
MsgBox "Heading not found in row 1"
GoTo Again
End If
Set Fsheet = ActiveSheet
icol = ColHeadCell.Column
'loop through values in selected column
For iRow = 2 To Fsheet.Cells(65536, icol).End(xlUp).Row
If Not SheetExists(CStr(Fsheet.Cells(iRow, icol).Value)) Then
Set Dsheet = Worksheets.Add(after:=Worksheets(Worksheets.Count))
Dsheet.Name = CStr(Fsheet.Cells(iRow, icol).Value)
Fsheet.Rows(1).Copy Destination:=Dsheet.Rows(1)
Else
Set Dsheet = Worksheets(CStr(Fsheet.Cells(iRow, icol).Value))
End If
Lrow = Dsheet.Cells(65536, icol).End(xlUp).Row
Fsheet.Rows(iRow).Copy Destination:=Dsheet.Rows(Lrow + 1)
Next iRow
End Sub
Function SheetExists(SheetId As Variant) As Boolean
' This function checks whether a sheet (can be a worksheet,
' chart sheet, dialog sheet, etc.) exists, and returns
' True if it exists, False otherwise. SheetId can be either
' a sheet name string or an integer number. For example:
' If SheetExists(3) Then Sheets(3).Delete
' deletes the third worksheet in the workbook, if it exists.
' Similarly,
' If SheetExists("Annual Budget") Then Sheets("Annual Budget").Delete
' deletes the sheet named "Annual Budget", if it exists.
Dim sh As Object
On Error GoTo NoSuch
Set sh = Sheets(SheetId)
SheetExists = True
Exit Function
NoSuch:
If Err = 9 Then SheetExists = False Else Stop
End Function
Change your Sub to:
Sub SplitToWorksheets_step4(SheetName as String)
and in the line:
Set Fsheet = ActiveSheet
to:
Set Fsheet = Worksheets(SheetName)
on a different page but this selects the active page, I want it to run
this macro on a specific sheet.
Well that is simple enough.
Set your Worksheet Object to a specific Sheet.Name - eg:
Dim Fsheet As Worksheet: Set Fsheet = Sheets("Your sheet name")
In a more practical usage, you could for example pass the sheet name as a procedure argument:
Private Sub SplitToWorksheets_step4(ByVal sheetName as String)
Dim fsheet as Worksheet: Set fsheet = Sheets(sheetName)
' ... do something
End Sub
Last but not least a practical way to apply a macro for every Worksheet:
Private Sub for_every_ws()
Dim ws as Worksheet
For Each ws In ThisWorkbook.Sheets
ws.Range("A1") = "I was here!" ' i.e.
Next ws
End Sub

Searching for text in excel, use an offset, and then a logical test, to output a string of text

The main goal of this is too search for "MRQ", once "MRQ" is found have the code do an offset down one row where it then tests if the product is > benchmark then output "over" Else output "Under"
Here is the code that I have so far but I am having trouble inputting the second if clause.
Sub FindMRQ()
Dim Sh As Worksheet
Dim MRQ As Range
Dim Product As Range
Dim Benchmark As Range
For Each Sh In ThisWorkbook.Worksheets
With Sh.UsedRange
Set MRQ = .Cells.Find(What:="MRQ")
If Not MRQ Is Nothing Then
MRQ.Offset(rowOffset:=1, columnOffset:=0).Activate
Do Until MRQ Is Nothing
ActiveCell.Value = "YAY!"
Set MRQ = .FindNext(MRQ)
Loop
End If
'If Product.Range > Benchmark.Range Then
'"Outperformed" Else
'If Product.Range < Benchmark.Range Then
'"Underperformed"
' End If
End With
Set MRQ = Nothing
Next
End Sub
Example of the Data Set
What you need to do is to put your If block before your Do Until loop but after the selection of the cell you are wanting to check. Something like this:
Sub FindMRQ()
Dim Sh As Worksheet
Dim MRQ As Range
Dim Product As Range
Dim Benchmark As Range
For Each Sh In ThisWorkbook.Worksheets
With Sh.UsedRange
Set MRQ = .Cells.Find(What:="MRQ")
If Not MRQ Is Nothing Then
MRQ.Offset(rowOffset:=1, columnOffset:=0).Select
If MRQ.Value > Benchmark.Value Then
MsgBox "Outperformed"
ElseIf MRQ.Value < Benchmark.Value Then
MsgBox "Underperformed"
End If
Do Until MRQ Is Nothing
ActiveCell.Value = "YAY!"
Set MRQ = .FindNext(MRQ)
Loop
End If
End With
Set MRQ = Nothing
Next Sh
End Sub
You will probably still need to tweak this to get it exactly right.
You can use a formula instead of VBA:
=IF(HLOOKUP("MQR",$A$1:$D$4,MATCH("Product",$A$1:$A$4,0),0)>HLOOKUP("MQR",$A$1:$D$4,MATCH("Benchmark",$A$1:$A$4,0),0),"Outperformed","Underperformed")

Excel VBA activate worksheet

I need to activate a specific worksheet. The code is meant to create worksheets with a specif name. I need to paste something from a another worksheet into all these newly created worksheets. The code that I'm using is below. But I'm having a hard time activating the newly created worksheet to paste what I want.
Sub octo()
'Dim ws As Worksheet
Dim Ki As Range
Dim ListSh As Range
Workbooks.Open ("C:\Users\Dash\Dropbox\Randika\Misc\Emmash timesheets\timesheet.xlsx")
With Worksheets("PPE 05-17-15")
Set ListSh = .Range("B4:B" & .Cells(.Rows.Count, "B").End(xlUp).Row)
End With
On Error Resume Next
For Each Ki In ListSh
If Len(Trim(Ki.Value)) > 0 Then
If Len(Worksheets(Ki.Value).Name) = 0 Then
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = Ki.Value
'open template
Workbooks.Open ("C:\Users\Dash\Dropbox\Randika\Misc\Emmash timesheets\octo_template.xls")
Range("A1:L31").Select
Selection.Copy
Worksheets(Ki.Value).Activate
If ThisWorkbook.Saved = False Then
ThisWorkbook.Save
End If
End If
End If
Next Ki
End Sub
Both Workbooks.Open and Worksheets.Add return references to the opened and added objects, which you can use to directly access and modify them - and in your case, to paste data.
Example:
Dim oSourceSheet As Worksheet
Dim oTargetSheet As Worksheet
Set oSourceSheet = Sheet1 'Set reference to any sheet, Sheet1 in my example
Set oTargetSheet = Worksheets.Add(After:=Worksheets(Worksheets.Count))
oSourceSheet.Range("A1:L31").Copy
oTargetSheet.Paste
Set oSourceSheet = Nothing
Set oTargetSheet = Nothing
I think that is what you need. As what been mentioned by chris, there is no need Activate or Select. Hope the following code solve your problem.
Option Explicit
Dim MyTemplateWorkbook As Workbook
Dim MyDataWorkbook As Workbook
Dim MyTemplateWorksheet As Worksheet
Dim MyDataWorksheet As Worksheet
Dim MyNewDataWorksheet As Worksheet
Dim CurrentRange As Range
Dim ListRange As Range
Sub AddWSAndGetData()
Set MyTemplateWorkbook = Workbooks.Open("C:\Users\lengkgan\Desktop\Testing\MyTemplate.xlsx")
Set MyTemplateWorksheet = MyTemplateWorkbook.Sheets("Template")
Set MyDataWorkbook = Workbooks.Open("C:\Users\lengkgan\Desktop\Testing\MyData1.xlsx")
Set MyDataWorksheet = MyDataWorkbook.Sheets("PPE 05-17-15")
Set ListRange = MyDataWorksheet.Range("B4:B" & MyDataWorksheet.Cells(Rows.Count, "B").End(xlUp).Row)
Application.ScreenUpdating = False
On Error Resume Next
For Each CurrentRange In ListRange
If Len(Trim(CurrentRange.Value)) > 0 Then
If Len(MyDataWorksheet(CurrentRange.Value).Name) = 0 Then
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = CurrentRange.Value
Set MyNewDataWorksheet = MyDataWorkbook.Sheets(ActiveSheet.Name)
MyNewDataWorksheet.Range("A1:L31").Value = MyTemplateWorksheet.Range("A1:L31").Value
If MyDataWorkbook.Saved = False Then
MyDataWorkbook.Save
End If
End If
End If
Next CurrentRange
MyTemplateWorkbook.Close (False) 'Close the template without saving
End Sub

Worksheet_Change Macro - Changing multiple cells

I wrote a macro and while it works, functionally its not what is needed. It's an interactive checklist that breaks down multiple areas of machines and if they are working checks them off and then this updates a master list with multiple sections. However, it only works with one cell at a time and it needs to be able to work with multiple cells at a time (both with rows and columns). Here's my current code:
'Updates needed:
' Make so more than one cell works at a time
' in both x and y directions
Private Sub Worksheet_Change(ByVal Target As Excel.range)
Dim wb As Workbook
Dim mWS As Worksheet
Dim conName As String
Dim mCol As range
Dim mCon As Integer
Dim count As Long
Dim cell As range
Dim y As String
count = 1
y = ""
Set wb = ActiveWorkbook
Set mWS = wb.Sheets("Master")
Set mCol = mWS.range("B:B")
mCon = 0
'Selects the name of the string value in which we need to search for in master list
If Target.Column < 100 Then
ThisRow = Target.Row
conName = ActiveSheet.Cells(ThisRow, "B")
y = Target.Value
End If
'search for matching string value in master list
For Each cell In mCol
If cell.Value = conName Then
mCon = count
Exit For
End If
count = count + 1
Next
'mark as "x" in Master list
Dim cVal As Variant
Set cVal = mWS.Cells(count, Target.Column)
cVal.Value = y
End Sub
What is happening - If I drag down "x" for multiple rows or columns my codes breaks at y = Target.Value and will only update the cell I first selected and its counterpart on the master list. What it should do is if I drag and drop the "x" onto multiple rows of columns it should update all of them in the sheet I'm working on and the master list. I only set up the macro for one cell at a time and I have no idea how to set it up for dragging and dropping the "x" value for multiple rows
I think you need a For ... Each iteration over the Target in order to work with multiple cells. As Michael noted in the comments, the _Change event fires only once, but the Target reflects all cell(s) that changed, so you should be able to iterate over the Target range. I tested using this simple event handler:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim myRange As Range
Dim myCell As Range
Set myRange = Target
For Each myCell In myRange.Cells
Debug.Print myCell.Address
Next
End Sub
I am not able to test obviously on your data/worksheet, but I think it should put you on the right track.
Private Sub Worksheet_Change(ByVal Target As Excel.range)
Dim wb As Workbook
Dim mWS As Worksheet
Dim conName As String
Dim mCol As range
Dim mCon As Integer
Dim count As Long
Dim cell As range
Dim y As String
count = 1
y = ""
Set wb = ActiveWorkbook
Set mWS = wb.Sheets("Master")
Set mCol = mWS.range("B:B")
mCon = 0
'Add some new variables:
Dim myRange as Range
Dim myCell as Range
Set myRange = Target
Application.EnableEvents = False '## prevents infinite loop
For each myCell in myRange.Cells
If myCell.Column < 100 Then
ThisRow = myCell.Row
conName = ActiveSheet.Cells(ThisRow, "B")
y = myCell.Value
End If
'search for matching string value in master list
For Each cell In mCol
If cell.Value = conName Then
mCon = count
Exit For
End If
count = count + 1
Next
'mark as "x" in Master list
Dim cVal As Variant
Set cVal = mWS.Cells(count, Target.Column)
cVal.Value = y
Next
Application.EnableEvents = True '## restores event handling to True
End Sub
You need to iterate through the cells using a ForEach loop.
Also, you may be better using the Selection object rather than Target
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
For Each cell In Selection
Debug.Print cell.Address
Next cell
Application.EnableEvents = True
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Exit Sub