VBA: Selecting range by variables - vba

I want to select the formatted range of an Excel sheet.
To define the last and first row I use the following functions:
lastColumn = ActiveSheet.UsedRange.Column - 1 + ActiveSheet.UsedRange.Columns.Count
lastRow = ActiveSheet.UsedRange.Rows(ActiveSheet.UsedRange.Rows.Count).Row
In the next step I want to select this area:
Formula should look like this:
Range(cells(1, 1), cells(lastRow, lastColumn).Select
However, this is not working. Maybe somebody has an idea what is wrong with it. Thanks a lot!

I recorded a macro with 'Relative References' and this is what I got :
Range("F10").Select
ActiveCell.Offset(0, 3).Range("A1:D11").Select
Heres what I thought : If the range selection is in quotes, VBA really wants a STRING and interprets the cells out of it so tried the following:
Dim MyRange as String
MyRange = "A1:D11"
Range(MyRange).Select
And it worked :) ie.. just create a string using your variables, make sure to dimension it as a STRING variables and Excel will read right off of it ;)
Following tested and found working :
Sub Macro04()
Dim Copyrange As String
Startrow = 1
Lastrow = 11
Copyrange = "A" & Startrow & ":D" & Lastrow
Range(Copyrange).Select
End Sub

I ran into something similar - I wanted to create a range based on some variables. Using the Worksheet.Cells did not work directly since I think the cell's values were passed to Range.
This did work though:
Range(Cells(1, 1).Address(), Cells(lastRow, lastColumn).Address()).Select
That took care of converting the cell's numerical location to what Range expects, which is the A1 format.

If you just want to select the used range, use
ActiveSheet.UsedRange.Select
If you want to select from A1 to the end of the used range, you can use the SpecialCells method like this
With ActiveSheet
.Range(.Cells(1, 1), .Cells.SpecialCells(xlCellTypeLastCell)).Select
End With
Sometimes Excel gets confused on what is the last cell. It's never a smaller range than the actual used range, but it can be bigger if some cells were deleted. To avoid that, you can use Find and the asterisk wildcard to find the real last cell.
Dim rLastCell As Range
With Sheet1
Set rLastCell = .Cells.Find("*", .Cells(1, 1), xlValues, xlPart, , xlPrevious)
.Range(.Cells(1, 1), rLastCell).Select
End With
Finally, make sure you're only selecting if you really need to. Most of what you need to do in Excel VBA you can do directly to the Range rather than selecting it first. Instead of
.Range(.Cells(1, 1), rLastCell).Select
Selection.Font.Bold = True
You can
.Range(.Cells(1,1), rLastCells).Font.Bold = True

You're missing a close parenthesis, I.E. you aren't closing Range().
Try this Range(cells(1, 1), cells(lastRow, lastColumn)).Select
But you should really look at the other answer from Dick Kusleika for possible alternatives that may serve you better. Specifically, ActiveSheet.UsedRange.Select which has the same end result as your code.

you are turning them into an address but Cells(#,#) uses integer inputs not address inputs so just use lastRow = ActiveSheet.UsedRange.Rows.count and lastColumn = ActiveSheet.UsedRange.Columns.Count

I tried using:
Range(cells(1, 1), cells(lastRow, lastColumn)).Select
where lastRow and lastColumn are integers, but received run-time error 1004. I'm using an older VB (6.5).
What did work was to use the following:
Range(Chr(64 + firstColumn) & firstRow & ":" & Chr(64 + lastColumn) & firstColumn).Select.

Related

Range from first empty row to last filled row [duplicate]

I want to select the formatted range of an Excel sheet.
To define the last and first row I use the following functions:
lastColumn = ActiveSheet.UsedRange.Column - 1 + ActiveSheet.UsedRange.Columns.Count
lastRow = ActiveSheet.UsedRange.Rows(ActiveSheet.UsedRange.Rows.Count).Row
In the next step I want to select this area:
Formula should look like this:
Range(cells(1, 1), cells(lastRow, lastColumn).Select
However, this is not working. Maybe somebody has an idea what is wrong with it. Thanks a lot!
I recorded a macro with 'Relative References' and this is what I got :
Range("F10").Select
ActiveCell.Offset(0, 3).Range("A1:D11").Select
Heres what I thought : If the range selection is in quotes, VBA really wants a STRING and interprets the cells out of it so tried the following:
Dim MyRange as String
MyRange = "A1:D11"
Range(MyRange).Select
And it worked :) ie.. just create a string using your variables, make sure to dimension it as a STRING variables and Excel will read right off of it ;)
Following tested and found working :
Sub Macro04()
Dim Copyrange As String
Startrow = 1
Lastrow = 11
Copyrange = "A" & Startrow & ":D" & Lastrow
Range(Copyrange).Select
End Sub
I ran into something similar - I wanted to create a range based on some variables. Using the Worksheet.Cells did not work directly since I think the cell's values were passed to Range.
This did work though:
Range(Cells(1, 1).Address(), Cells(lastRow, lastColumn).Address()).Select
That took care of converting the cell's numerical location to what Range expects, which is the A1 format.
If you just want to select the used range, use
ActiveSheet.UsedRange.Select
If you want to select from A1 to the end of the used range, you can use the SpecialCells method like this
With ActiveSheet
.Range(.Cells(1, 1), .Cells.SpecialCells(xlCellTypeLastCell)).Select
End With
Sometimes Excel gets confused on what is the last cell. It's never a smaller range than the actual used range, but it can be bigger if some cells were deleted. To avoid that, you can use Find and the asterisk wildcard to find the real last cell.
Dim rLastCell As Range
With Sheet1
Set rLastCell = .Cells.Find("*", .Cells(1, 1), xlValues, xlPart, , xlPrevious)
.Range(.Cells(1, 1), rLastCell).Select
End With
Finally, make sure you're only selecting if you really need to. Most of what you need to do in Excel VBA you can do directly to the Range rather than selecting it first. Instead of
.Range(.Cells(1, 1), rLastCell).Select
Selection.Font.Bold = True
You can
.Range(.Cells(1,1), rLastCells).Font.Bold = True
You're missing a close parenthesis, I.E. you aren't closing Range().
Try this Range(cells(1, 1), cells(lastRow, lastColumn)).Select
But you should really look at the other answer from Dick Kusleika for possible alternatives that may serve you better. Specifically, ActiveSheet.UsedRange.Select which has the same end result as your code.
you are turning them into an address but Cells(#,#) uses integer inputs not address inputs so just use lastRow = ActiveSheet.UsedRange.Rows.count and lastColumn = ActiveSheet.UsedRange.Columns.Count
I tried using:
Range(cells(1, 1), cells(lastRow, lastColumn)).Select
where lastRow and lastColumn are integers, but received run-time error 1004. I'm using an older VB (6.5).
What did work was to use the following:
Range(Chr(64 + firstColumn) & firstRow & ":" & Chr(64 + lastColumn) & firstColumn).Select.

copy paste filtered data not working as expected

First things first. I am very new to VBA.
Secondly, I googled my ass of and I honestly don't get to the bottom of it. Mostly because the code is adapted to my needs based on googleing i did (copy/paste of code).
To my problem. I have a sheet(Raw Data) with lots of columns(A:AN) and lots of rows(160000) that gets updated every now and then. I want to filter the dataset based on the criteria from a few columns(A & B), and the copy/paste the data in a different sheet(Scatter Raw) starting from column A. I also do not want to copy the header from "Raw Data" and start pasting in "Scatter Sheet" also below the header -> in this case 2 rows.
I have two issues for now:
Based on the filters I do, I will get 17267 rows in "Raw Data". If I simply do a select and copy then I copy only the filtered data. But the moment I paste the data somehow I suddenly get 18362 rows, even though they are empty. I can see this by the fact that the scroll bar goes down. I used this way of copying because sometimes I want to be able to append the copied data based on value set in a different cell. What am I doing here wrong, or what is happening?
I have more sheets inside the workbook. If I do not have the Raw Data worksheet selected I get an error like "Application-defined or object-defined error" on the "Set rng = " line which I don't get. In other test I also got a different error, but that was because the Range was based on the active sheet and not the one I needed. Why is this happening, since the filters are correctly set?
The values from column N should all be divided by 1000. I guess I have no other way then using a temporary copy column, divide it by 1000 in a new column and then copy/paste the new values to the location I need in, right?
Just one last mention, the code is running in a Module and will be later assigned to a button.
Sub Copy()
Dim destTrSheet As Worksheet
Dim sctrSheet As Worksheet
Set destTrSheet = ThisWorkbook.Worksheets("Data Raw")
Set sctrSheet = ThisWorkbook.Worksheets("Scatter Raw")
With destTrSheet
.Range("A:A").AutoFilter field:=1, Criteria1:="VF", Operator:=xlFilterValues
.Range("B:B").AutoFilter field:=2, Criteria1:="CITY", Operator:=xlFilterValues
Set Rng = .Range("N2").Resize(Cells(Rows.count, "N").End(xlUp).Row - 1)
Rng.Copy
sctrSheet.Range("A" & Rows.count).End(xlUp).Offset(1, 0).PasteSpecial (xlPasteValues)
Set Rng = .Range("X2").Resize(Cells(Rows.count, "N").End(xlUp).Row - 1)
Rng.Copy
sctrSheet.Range("B" & Rows.count).End(xlUp).Offset(2, 0).PasteSpecial (xlPasteValues)
End With
End Sub
The issues you mentioned
Discrepancy between manual copy and code copy could be caused by the offsets used:
Col A .Offset(1, 0).PasteSpecial - 1 row below last used row
Col B .Offset(2, 0).PasteSpecial - 2 rows below last used row
The error is caused by .Range("N2") vs (Cells(Rows.count, "N")
.Range("N2") is explicitly qualified because of the dot (.) - refers to "Data Raw"
Cells(Rows.count, "N") is implicitly referring to ActiveSheet (missing .)
If column N should be divided by 1000
Yes, a helper column can be used, as in the code bellow
Another way: copy the column to an array, divide each value, then paste it back
If column N contains strings, the division will generate cell errors:
Option Explicit
Public Sub CopyRawToScatter()
Dim wsR As Worksheet: Set wsR = ThisWorkbook.Worksheets("Data Raw")
Dim wsS As Worksheet: Set wsS = ThisWorkbook.Worksheets("Scatter Raw")
Dim lrR As Long: lrR = wsR.Cells(wsR.Rows.Count, "A").End(xlUp).Row
Dim lrS As Long: lrS = wsS.Cells(wsS.Rows.Count, "A").End(xlUp).Row + 1
With wsR
Dim fRng As Range: Set fRng = .Range(.Cells(1, "A"), .Cells(lrR, "B"))
Dim rngN As Range: Set rngN = .Range(.Cells(2, "N"), .Cells(lrR, "N"))
Dim rngX As Range: Set rngX = .Range(.Cells(2, "X"), .Cells(lrR, "X"))
Dim cRng As Range: Set cRng = Union(rngN, rngX)
End With
Application.ScreenUpdating = False
fRng.AutoFilter field:=1, Criteria1:="VF", Operator:=xlFilterValues
fRng.AutoFilter field:=2, Criteria1:="CITY", Operator:=xlFilterValues
If fRng.SpecialCells(xlCellTypeVisible).CountLarge > 2 Then
cRng.Copy
wsS.Cells(lrS, "A").PasteSpecial xlPasteValues
With wsS
Dim vis As Long: vis = .Cells(.Rows.Count, "A").End(xlUp).Row
Dim lcS As Long: lcS = .Cells(lrS, "A").End(xlToRight).Column + 1
Dim divA As Range: Set divA = .Range(.Cells(lrS, "A"), .Cells(vis, "A"))
Dim divX As Range: Set divX = .Range(.Cells(lrS, lcS), .Cells(vis, lcS))
divX.Formula = "=" & .Cells(lrS, 1).Address(RowAbsolute:=False) & " / 1000"
divA.Value2 = divX.Value2
divX.ClearContents
End With
End If
wsR.UsedRange.AutoFilter
Application.ScreenUpdating = False
End Sub
Other issues
Potential conflict between your Sub name (Copy()) with the built-in Range.Copy Method
The 2 AutoFilter lines are invalid
.Range("A:A").AutoFilter field:=1, Criteria1:="VF", Operator:=xlFilterValues
.Range("B:B").AutoFilter field:=2, Criteria1:="CITY", Operator:=xlFilterValues
If your code works you probably modified it when posting the question; they should be
.Range("A:B").AutoFilter field:=1, Criteria1:="VF", Operator:=xlFilterValues
.Range("A:B").AutoFilter field:=2, Criteria1:="CITY", Operator:=xlFilterValues
You don't need brackets for .PasteSpecial (xlPasteValues)

Copying and pasting info from one sheet to another using lastcol & lastrow

I understand copying and pasting is not a recommended way to go, however am proceeding with it. I currently have a amount of data on each sheet. I am avoiding the final row when I copy the data from the rows(Works perfect). And I am avoiding the final column when I copy the columns(Works perfect).
LastRow:
Lastrow = Cells(Rows.count, "A").End(xlUp).Row - 1
LastColumn:
With ActiveSheet
lastcol = .Cells(4, .Columns.count).End(xlToLeft).Column - 1
End With
I however need to now copy the data. I can copy the rows perfectly fine. However the columns is where I am getting caught up on. I'm quiet new to the Copy/Paste method, and am unsure how to go along of fixing this as I always used a range of cell names.
This will give me the rows needed, and all the columns needed as I am copying past the last column.
Sheets("Sheet2").Range("A4:AZ" & Lastrow).Copy Sheets(PlantArr(e)).Range("A1") 'Copy info to new sheet
How can I convert this so that AZ is actually the last column needed? Is there an easy way to go about this or will I need to set up a case to convert column number to cell value?
Answer:
gAddress = Split(Cells(1, lastcol).Address(True, False), "$") 'Converts Lastcol number into alphabet character
letter = gAddress(0)
Sheets("Sheet2").Range("A4:" & letter & Lastrow).Copy Sheets(PlantArr(e)).Range("A1") 'Copy info to new sheet
After a bit of digging around, I figured out that you can pull an address from a column value. I hope the code below helps anyone who would like to convert a column number to an alphabet column.
Dim gAddress
gAddress = Split(Cells(1, lastcol).Address(True, False), "$")
letter = gAddress(0)
It's possible to specify the copy Range by explicitly stating which cells compose that range. Using 'With', you can write something like this to accomplish what you need:
Sub copy_col_and_rows()
Dim Lastrow As Integer: Dim lastcol As Integer:
With ActiveSheet
Lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row - 1
lastcol = .Cells(4, Columns.Count).End(xlToLeft).Column - 1
End With
With Sheets("Sheet2")
.Range(.Cells(1, 4), .Cells(Lastrow, lastcol)).Copy Destination:=Sheets(PlantArr(e)).Range("A1")
'Where .Cells(1,4) corresponds to "A4"
End With
End Sub

Trying to select with .Range( .Cells( #,#) ) won't work in VBA

I'm trying to select a range of cells and I need to use LastRow as the variable that defines the last row of the selection. Here is my code (well the necessary part anyway, it's part of a large loop..):
Dim LastRow As Long
LastRow = FlowWkbs(f).Sheets(w).Cells(7,sht.Columns.Count).End(xlToLeft).Column
With FlowWkbs(f).Sheets(w)
.Range("G1").FormulaR1C1 = "Normal_0to1"
.Range("G2").FormulaR1C1 = "0"
.Cells(LastRow, 7).FormulaR1C1 = "1"
.Range(.Cells(2, 7), .Cells(LastRow, 7)).Select
.Cells(LastRow, 7).Activate
End With
I am getting "Run-time error '1004': Application-defined or object-defined error" as an error on the line:
.Range(.Cells(2, 7), .Cells(LastRow, 7)).Select
It's funny because it does fine with assigning .Cells(LastRow,7) to a value of 1. But it can't handle the selection of G2:G65335 (that happens to be my last row) In the form that I want it to be in. I need to have the LastRow variable change for each iteration of FlowWkbs(f) and Sheets(w) so I need to use the Range(Cells(#%#%)) method as opposed to Range("G2:G324")
I've looked at a number of other questions/topics on here and they all suggest to add the Workbooks.Worksheets. prefix to the Range and Cells, but the With should take care of all that.
Any ideas?
You're finding the last column and putting the value in LastRow, then using that as the row argument to .Cells. If you need the last row, do something like this:
With FlowWkbs(f).Sheets(w)
LastRow = .Cells(.Rows.Count, 7).End(xlUp).Row
.Range("G1").FormulaR1C1 = "Normal_0to1"
.Range("G2").FormulaR1C1 = "0"
.Cells(LastRow, 7).FormulaR1C1 = "1"
.Range(.Cells(2, 7), .Cells(LastRow, 7)).Select
.Cells(LastRow, 7).Activate
End With
If you need to work with a range of columns, switch the argument order in your calls to .Cells (and rename the misleading variable).
I copy and pasted the wrong variable definition for LastRow, the actual LastRow in my code did find the last row and not column. So the issue wasn't that I was getting the last column. Also, that really wouldn't have answered my question because the variable LastRow would have still had a number value and the same error would've shown up..
The problem was that the Worksheet that I was trying to run the Range.select on wasn't selected first. This is the corrected code (without the row/column typo):
Dim LastRow As Long
LastRow = sht.Cells(sht.Rows.Count, "A").End(xlUp).Row
With FlowWkbs(f).Sheets(w)
.Range("G1").FormulaR1C1 = "Normal_0to1"
.Range("G2").FormulaR1C1 = "0"
.Cells(LastRow, 7).FormulaR1C1 = "1"
.Select
.Range(.Cells(2, 7), .Cells(LastRow, 7)).Select
.Cells(LastRow, 7).Activate
End With
Voila!

Matching and inserting records in excel

I have two sheets of data. One sheet has Primary Id with 4 fields and other has primary Id with 2 fields.
Sheet A Sheet B
ID Name Price Type Category ID Name Price
1 S Normal 2 Aus 500
2 N Default 1 Ind 400
Basically I need to match the ID of both sheets and copy the corresponding Name and Price in sheet A form Sheet B. I have tried the following code,
Sub Copy()
lastrowA = Worksheets("SheetA").Cells(Rows.Count, "A").End(xlUp).Row + 1
Set rngA = Range("A2" & lastrowA)
lastrowB = Worksheets("SheetB").Cells(Rows.Count, "A").End(xlUp).Row + 1
Set rngB = Range("A2" & lastrowB)
For Each x In rngB
For Each y In rngA
If x.Value() = y.Value Then
' Copy paste name and price form B to A
End If
Next
Next
End Sub
It's never a good idea to use a reserved word as the name of your macro. Particularly so if you plan to use a .Copy operation within the macro.
Sub MyCopy()
Dim lastrowA As Long
With Worksheets("SheetA")
lastrowA = .Cells(Rows.Count, "A").End(xlUp).Row
With .Range("B2:C" & lastrowA)
.Formula = "=IFERROR(VLOOKUP($A2, 'SheetB'!$A:$C, COLUMN(B:B), FALSE), """")"
.Value = .Value
End With
End With
End Sub
That bulk populates the entire region with the appropriate formula without looping then converts the returned values to raw values. Any non-matches will be blank rather than #N/A errors.
Does it have to be done without using formulas? I'm not sure if I'm missing something, but surely you can just use either a Vlookup or an Index Match?
If entering the formula from VBA:
Cells(2,2).FormulaR1C1 = "=INDEX(Sheet2!R2C2:R3C3,MATCH(RC[-1],Sheet2!RC[-1]:R[1]C[-1],0),1)"
Cells(2,3).FormulaR1C1 = "=INDEX(Sheet2!R2C2:R3C3,MATCH(RC[-2],Sheet2!R2C1:R3C1,0),2)"
Then you can find the last row in the ID column on sheet 1, and fill the formula down both of the columns. Once the formula has been filled down, just copy and paste as values.
Dim lstRow As Long
lstRow = Sheets("Sheet 1").Cells(Rows.Count, 1).End(xlUp).Row '' find last row
Range(Cells(2, 2), Cells(lstRow, 3)).FillDown
Range(Cells(2, 2), Cells(lstRow, 3)).Copy
Cells(2, 2).PasteSpecial Paste:=xlPasteValues
Edit: You can use the lstRow variable within the VBA formula to make sure the formula is covering the whole range everytime the automation is run. You can use the 'Record Macro' button within excel to get the code for a formula, if you are not comfortable creating them yourself.
The Problem with your code is that
Set rngA = Range("A2" & lastrowA)
evaluates to Range("A25") for lastRowA=5.
If you want to address multiple cells, use
Set rngA = Range("A2:A" & lastrowA)
to get Range("A2:A5") for lastRowA = 5.
Besides that, formulas as already mentioned are an elegant solution as well.