Avoid repeating selection when I have empty cells in range - vba

I am a bit of a VBA novice so was hoping someone could help with the following.
I need to select (and clear the contents of) all cells in a workbook before pasting new data in. Problem is when I use the (xlToLeft) command it stops when it encounters an empty cell.
I have the code below (which works) but wanted to see if there is a better way, which I am sure there will be.
Sheets("TEST").Activate
Range("BB3").Select
Range(Selection, Selection.End(xlDown)).Select
Range(Selection, Selection.End(xlToLeft)).Select
Range(Selection, Selection.End(xlToLeft)).Select
Range(Selection, Selection.End(xlToLeft)).Select
Range(Selection, Selection.End(xlToLeft)).Select
Range(Selection, Selection.End(xlToLeft)).Select
Range(Selection, Selection.End(xlToLeft)).Select
Range(Selection, Selection.End(xlToLeft)).Select
Range(Selection, Selection.End(xlToLeft)).Select
Range(Selection, Selection.End(xlToLeft)).Select
Range(Selection, Selection.End(xlToLeft)).Select
Selection.ClearContents
Just for reference I have info in the cells to the right which I want to keep and the rows will change on each occasion I run the script.

Why not clear all cells instead of looking for the ones with something in them?
Sheets("TEST").Activate
Activesheet.cells.ClearContents

If you wish to clear a set range:
Worksheets("Test1").Range("A1:I60").ClearContents
If you wish to clear a range based on a value in a row (* is a wildcard, you can substitute it for a text string if required):
Dim lRow as Integer
lRow = Worksheets("Test1").Range("I:I").Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Worksheets("Test1").Range("A1:I" & lRow).ClearContents
Best practice is to avoid using .Select/.Activate and any other indirect reference to a workbook/worksheet/range/cell and provide a fixed reference. Using any non-fixed reference slows down code execution but also makes it hard to review the code at a future date.

first things first - you should avoid using Select altogether.
It's a rather bad coding habit in VBA that's going to do you more harm
than good in the long run. I'm not going to go into too much detail
here, but I'd recommend reading (thoroughly) this question here:
How to avoid using Select in Excel VBA
I've created the following procedure:
clear_til_empty(ByVal inrow as Long)
Finds last non-empty cell in a specified row (inrow) and removes the cells in their respective columns.
Private Sub clear_til_empty(ByVal inrow As Long)
' clears all the cell contents until it hits an empty cell
Dim ws As Worksheet: Set ws = Sheets("Sheet1")
Dim lc As Long ' last empty column
lc = ws.Cells(inrow, Columns.Count).End(xlToLeft).Column
' ws.Range("1:" & lc).ClearContents // didn't work as expected
For i = 1 To lc
ws.Columns(i).ClearContents
Next i
End Sub
So for example, if we wanted to remove data from all columns, until an empty cell is hit in the first row, we would use the following invokation.
clear_til_empty(1)
Obviously, you can pass a variable to it instead.

Related

how to set range for a dynamic area and pasting with filters on in vba

I'm having some trouble with a 2 step problem.
The first part is setting a range to constantly changing data. I've been trying to categorize claims on a worksheet that has data that is being added to the same sheet daily, so the last active cell keeps changing. For instance my issue with the specific line of code is as follows.
Columns(“D:D”).Select
Selection.End(xlDown).Select
ActiveCell.Offset(1).Select
ActiveCell.Offset(0, 91).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Columns(“D:D”).Select
Selection.End(xlDown).Select
ActiveCell.Offset(1).Select
ActiveSheet.Paste
ActiveSheet.Range("$A$1:$EE$3000”).AutoFilter Field:=22, Criteria1:= Array( _
"DUPLICATE PAID/CAPTURED CLAIM:MORE CURRENT REFILL EXISTS", _
"REFILL TOO SOON:CLAIM ALREADY PROCESSED FOR STORE, RX, DOS", _
"REFILL TOO SOON:DISPENSED TOO SOON"), Operator:=xlFilterValues
Columns(“A:A”).Select
Selection.End(xlDown).Select
Selection.Copy
ActiveCell.Offset(1).Select
ActiveSheet.Paste
But I'm now recognizing setting the end range to a value of $EE$2007 isn't working. The column of EE will always remain the same, however the row changes.
The second part has to do with filtering. There are around 56 different categories in a separate column, V which then get flagged with a keyword in column A. I've gotten as far as being able to do the filtering portion and copying from the next cell up, but I'm having trouble pasting that keyword down the next which is the next blank cell and down to the last active row, again which all happens in column A. Above is what I have so far.
And this is where I get stuck. I'm new to all this, and am hoping to learn if there is a better way to go about this.
I was able to get to the second part by changing the range from $EE$2007 to $EE$3000 which caused my data to go further, but it is a possibility.
Thanks in advance.

Excel VBA Macro: Autofill a list in a Weekday frequency

With this macro I am able to insert a new row at the bottom of a daily time-series data. The macro performs well, but even though I specified to fill the series with Weekdays and avoid weekends, it still not do so and fills with all the days of the week.
Any suggestions on what I might be missing?
Please also see the screenshot for a better understanding.
Thank a lot.
Sub Weekday_Data_Update()
Range("A2").Select
Selection.End(xlDown).Select
ActiveCell.Offset(-1, 0).Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Resize(3).Select
Selection.DataSeries Rowcol:=xlColumns, Type:=xlAutoFill, Date:=xlWeekday, _
Trend:=False
End sub
Example of how the macro is filling the dates wrongly
I am confused by all your up, down, across movement. To simply extend dates across a range only using weekdays would have syntax such as follows:
Option Explicit
Public Sub Test()
With Worksheets("Sheet1")
.Range("A2").AutoFill Destination:=.Range("A2:A7"), Type:=xlFillWeekdays
End With
End Sub
You can construct a fill to last used cell as follows:
.Range("A2").AutoFill Destination:=.Range(.Cells(2, "A"), .Cells(.Cells.SpecialCells(xlLastCell).Row, "A")), Type:=xlFillWeekdays
Just to break it down, the below code will print ONLY the Weekdays
Sub Weekday_Data_Update()
Dim startRange As Range
Dim stopRange As Range
'Specify the cell where the date starts
Set startRange = Sheets("Sheet1").Range("A2")
'Specify the cell until which you want weekdays to be displayed
Set stopRange = Sheets("Sheet1").Range("A2:A6")
startRange.Select
Selection.AutoFill Destination:=stopRange, Type:=xlFillWeekdays
End Sub
P.S: Not sure why you many range selections in your code.
According to MSDN, Date:=xlWeekday is only applicable when you use Type:=xlChronological
So, try
Selection.DataSeries Rowcol:=xlColumns, Type:=xlChronological, Date:=xlWeekday, _
Trend:=False
Refer to MSDN help for further details

Change from absolute to relative reference in Excel formula (VBA)

Unfortunately I couldn’t find the answer to the below in the other questions – my problem is related to copying and pasting a formula that would use relative cell references instead of dynamic ones.
The general problem with the workbook I’m working on is the fact that it contains of a couple of different sheets with a potential dynamic range change. To give a better outline:
The column the formula has to be in, is based on an offset cell – I cannot give it a static value – and starts in row 2;
The formula itself is as follows: =CONCATENATE(LEFT(AA2,13), “:”, RIGHT(AA2,5) (and the values to be concatenated will always appear in the AA column)
The macro is supposed to insert the formula in the offset cell, copy it and paste it in the entire column, so the cell in row 3 refers to cell AA3 and so on:
Sub Copy1()
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.End(xlToRight).Select
ActiveCell.Offset(, 1).Select
Dim rng as Range
Set rng = ActiveCell
rng.Select
rng.Offset(1, 0).Select
ActiveCell.FormulaR1C1 = "=CONCATENATE(LEFT(R2C27,13), "":"", RIGHT(R2C27,5))"
rng.Offset(1, 0).Select
Selection.Copy
Range(Selection, Selection.End(xlDown)).Select
ActiveSheet.PasteSpecial
Selection.EntireColumn.Select
Application.CutCopyMode = False
End Sub
The problem is – upon trying to insert exact name of the cell, the macro populates it with the following:
=CONCATENATE(LEFT(‘AA2’,13), "":"", RIGHT(‘AA2’,5))
Because of the quotation marks, the formula doesn’t work.
Using the reference R2C27 results with absolute values being copied and thus every single cell in the column refers to cell AA2.
Is there any possibility make it create, copy and paste relative reference instead of absolute?
R1C1 reference is usually not needed as relative references are auto-adjusted if you copy+paste the formula or assign it to multiple cells. For example:
Range("A2:A9").Formula = "=CONCATENATE(LEFT(AA2,13), "":"", RIGHT(AA2,5)"
If I understand you want the column reference to be always $AA (absolute) while the row reference be relative. You can try this:
ActiveCell.Formula = "=CONCATENATE(LEFT($AA2,13), "":"", RIGHT($AA2,5))"
Then the autofill will adjust automatically the row number while keeping the column at "$AA".
That said, refactor your code to get rid of the select stuff. Look how shorter it will be in addition to using "Explicit references" (just replace "Sheet1" with the actual name of your worksheet):
Sub Copy1()
Dim rng As Range
Set rng = Worksheets("Sheet1").Range("A1").End(xlToRight).Offset(1, 1)
rng.Formula = "=CONCATENATE(LEFT($AA2,13), "":"", RIGHT($AA2,5))"
rng.Copy rng.Parent.Range(rng, rng.End(xlDown))
End Sub

VBS Excel Trying to select past empty cells in a macro

I'm currently trying to create a macro that will make a selection based on a few criteria. However, I am running into problems when it comes to selecting all data to the right of a current point due to certain cells being empty.
So far I have something like this:
rwcnt = WorksheetFunction.CountA(Range("A:A"))
lastc = Cells(1, Columns.Count).End(xlToLeft).Column
Dim i As Long
For i = 2 To rwcnt
If IsNumeric(Cells(i, 1).Value) <> True Then
Range(Cells(i, 1).Address, Cells(i, 1).End(xlDown).Address).Select
Range(Selection, lastc).Select
Exit For
End If
Next
This gives me an error method 'range' of object '_global' failed
previously, I had the last range line reading as:
Range(Selection, Selection.End(xlToRight)).Select
I tried this as I am starting from the left, however, some rows have blank cells which is where the selection would get stopped. Because of this I am trying to make a way to come in from the right and find my last column and then select to that. Any help would be greatly appreciated!
Range(Selection, lastc).Select
will fail because for this syntax Range is expecting a starting Range and an ending Range. (See https://msdn.microsoft.com/en-us/library/office/ff841254(v=office.15).aspx for syntax)
lastc is a number (specifically the number of the last used column).
You'd want to use something like the following instead:
Range(Selection, Cells(i, lastc)).Select
The parameters will depend on exactly what you want to select.

copy and paste to table in a more efficient way

I have an excel file with 2 sheets:
1.INFO (huuuge table, around 10.000 rows)
2.ADD INFO
The second is where my issue is. Basically it has 2 functions, either filter (based on 2 different criteria) and search for info in the table and display it on that sheet or add a new row to the table. The macros i made work fine, but it seems weird to me that 1 minute is too long for it to complete the task:
Sub Search_in_table()
Dim header As Range
Sheets("ADD INFO").Select
Range("A13").Select
Range(Selection, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Delete Shift:=xlUp
If Worksheets("ADD INFO").[Item_to_search] = "Cust_ID" Then
Sheets("INFO").Select
Set header = [A1:Y1]
With header
.AutoFilter Field:=6, Criteria1:=Worksheets("INFO").[What_I_Want]
End With
ElseIf Worksheets("ADD INFO").[Item_to_search] = "ASIN" Then
Sheets("INFO").Select
Set header = [A1:Y1]
With header
.AutoFilter Field:=4, Criteria1:=Worksheets("INFO").[What_I_Want]
End With
End If
ActiveSheet.AutoFilter.Range.Copy
Sheets("ADD INFO").Select
Worksheets("ADD INFO").[A13].PasteSpecial
Sheets("INFO").Select
header.Select
Selection.AutoFilter
Sheets("ADD INFO").Select
End Sub
And this is the one to add a new row:
Sub add_esc()
Sheets("ADD INFO").Select
Range("Y9:A9").Select
Selection.Copy
Sheets("INFO").Select
Range("A1").Select
Selection.End(xlDown).Select
ActiveCell.Offset(1, 0).Select
Selection.PasteSpecial
Sheets("ADD INFO").Select
Range("A9:Y9").Select
Selection.ClearContents
Is there a way to make them more efficient? Did i miss something? BTW, What_I_Want and Item_To_Search are just cells with names. An interesting fact is that during the writing of the code, i got very weird errors in things like "selection.paste", and that is why i was using a not common notation like "Worksheets("ADD INFO").[A13].PasteSpecial"
Any thoughts are greatly appreciated!!! Thanks in advance!
I'd recommend getting rid of "select" and "activate" wherever you can, should speed things up and ultimately avoid bugs. Are you able to just clear ALL cells on sheets("add info") for example? sheets("add info").cells.clear
As for the rest of the code: can't remember doing anything similiar but theory sounds OK... get rid of all those "selects" though, they just slow things down. Where you've got:
ActiveSheet.AutoFilter.Range.Copy
Sheets("ADD INFO").Select
Worksheets("ADD INFO").[A13].PasteSpecial
Instead just use this (and for extra credit, instead of using Sheets() refer directly to the sheets codename:
wsInfo.autofilter.range.copy wsAddInfo.cells(13,1) ' i.e. row 13, column 1 (col A)
Not sure about the rest of your question re: add rows etc, but using method above maybe you just need to increment that copy destination row? Or use application.worksheetfunction.counta(range) to find the last used cell rather than "select" etc...
There's the possibility that your method might actually be slower than just iterating over each row manually and copying where condition's met? Might be worth a try. Something like:
for each rgCell in rgList
if rgCell.offset(0,4) = stCrit1 _
and rgcell.offset(0,8) = stCrit2 then
rgcell.entirerow.copy wsAddInfo.cells(intDrawRow, intDrawCol)
intDrawRow = intDrawRow + 1
end if
next
And if all else fails, and i strongly recommend you get rid of all the "selects" in your code before doing this, use application.screenupdating = false at the start of your code, and application.screenupdating = true at the end of your code. Highly recommend error handling so that you set screenupdating back to true on errors too.
Cheers, Si