Very new to VBA. i am having trouble declaring a variable as a specific cell on a worksheet.
I have tried defining the cell by rows and columns but when I put a watch on the line it says Value is out of context. The variable is testname and it is in cell E2 of the worksheet I have set as the variable raw.
Sub findcomponents()
Dim raw As Worksheet
Dim res As Worksheet
Dim temp As Worksheet
Dim testname As String
Dim finalrow1 As Integer
Dim finalrow2 As Integer
Dim i As Integer
Set raw = Worksheets("rawdata")
Set res = Worksheets("resultcomponents")
Set temp = Worksheets("uploadtemplate")
testname = raw.Range("E2").Value
finalrow1 = raw.Range("A10000").End(xlUp).Row
finalrow2 = res.Range("A10000").End(xlUp).Row
For i = 2 To finalrow2
If res.Cells(i, 4) = testname Then
Range(Cells(i, 2), Cells(i, 4)).Copy
temp.Range("A10000").End(xlUp).Cells("A2").Paste
End If
Next i
End Sub
I expect the value to be the string in the E2 cell
Edit: I added the rest of the code. When I run it doesn't do anything.
It is supposed to take the string testname and loop through a list on the res worksheet and return the matches. I put a watch on the testname line because i thought it would show me that it was comparing the correct string and the Value in the watch line says
yes the paste line is incorrect. I also tried temp.Range("A10000").End(xlUp).Offset(1, 0).Paste and this makes it angry also.
The fix works with an edit on the worksheet name. But there is more than 1 match on the res worksheet. That is why I thought finding the the last row (but I should have offset 1 row) would return all the matches. This does work to return all values.
temp.Range("A10000").End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
Is there a better way to do this not using Range("A10000").End(xlUp)?
Your key problem is with the paste statement.
You can use one of the two following methods to paste (although it should be noted that there are others)
Range.Copy PasteRange
or
Range.Copy
PasteRange.PasteSpecial (paste type (values, formats, etc.))
This has also been updated with more standard variable blocks/variable names for Last Row calculations. Also, notice that I have also updated the Last Row calculation to be a little more dynamic. Lastly, swapping Integer for Long
Option Explicit
Sub findcomponents()
Dim raw As Worksheet: Set raw = ThisWorkbook.Sheets("rawdata")
Dim res As Worksheet: Set res = ThisWorkbook.Sheets("resultcomponents")
Dim temp As Worksheet: Set temp = ThisWorkbook.Sheets("uploadtemplate")
Dim testname As String
Dim LR1 As Long, LR2 As Long, LR3 As Long, i As Long
LR1 = raw.Range("A" & raw.Rows.Count).End(xlUp).Row '<-- This variable is never used??
LR2 = res.Range("A" & res.Rows.Count).End(xlUp).Row '<-- Updated for standard Last Row (LR) calculation
For i = 2 To LR2
If res.Cells(i, 4) = raw.Range("E2") Then
res.Range(res.Cells(i, 2), res.Cells(i, 4)).Copy '<-- Qaulified Ranges!!
LR3 = temp.Range("A" & temp.Rows.Count).End(xlUp).Offset(1).Row
temp.Range("A" & LR3).PasteSpecial xlPasteValues '<-- Correct Paste Method
End If
Next i
End Sub
The naming conventions used are just my preference. To an extent, you are free to name variables however you see fit
Related
I am trying to find a way to replace all values on the second tab of an Excel workbook with values from a table in a different tab 1 cell to the right of the corresponding value. On Sheet1 there are 2 columns. 1 is called ID and the second is called New ID. On Sheet2 there is a column called ID. I am looking for a way so that when I run a macro the values on Sheet2 will be replaced by the corresponding New ID from Sheet1. For example, on Sheet2 the first ID is ABC. On Sheet1 the corresponding New ID value for ABC is 123. I'd like the VBA script to replace all ABCs on Sheet2 with 123. I need this for varying amounts of data.
Sheet1
Sheet2
So far I've tried the following but it won't change the cells
Sub Test1()
Dim N As Long, L As Long
Dim rLook As Range
Sheets("Sheet1").Select
N = Cells(Rows.Count, "A").End(xlUp).Row
aryA = Range("A2:A" & N)
aryB = Range("B2:B" & N)
Sheets("Sheet2").Select
Set rLook = Range("A2:A" & N)
For L = 1 To N
rLook.Replace aryA(L, 1), aryB(L, 1)
Next L
End Sub
When I run the macro it only changes the same number of rows as Sheet1 so I am left with the following:
Result
After I run this I get an error that says subscript is out of range.
Your error is basically that you reuse N, which is the number of rows from sheet1 to define the range on sheet2.
So my advise is to use more explicit names for variables that explain what the variable "contains".
Furthermore if you don't use the implicit Cells(xxx) but the explicit one Thisworkbook.Worksheets("Sheet1") you can omit the selection of the sheets (and by that reduce the possibility for errors referencing the wrong range).
Plus: you can read both columns of sheet1 into one array
Option Explicit
Public Sub updateSheet2IDs()
Dim wsSource As Worksheet
Set wsSource = ThisWorkbook.Worksheets("Sheet1")
Dim wsTarget As Worksheet
Set wsTarget = ThisWorkbook.Worksheets("Sheet2")
Dim cntRowsSheet1 As Long
Dim arrSource As Variant
With wsSource
cntRowsSheet1 = .Cells(.Rows.Count, "A").End(xlUp).Row
'array includes both columns: arrsource(1,1) = A2, arrsource(1,2) = B2
arrSource = .Range("A2:B" & cntRowsSheet1)
End With
Dim cntRowsSheet2 As Long, rgTarget As Range
With wsTarget
cntRowsSheet2 = .Cells(.Rows.Count, "A").End(xlUp).Row
Set rgTarget = .Range("A2:A" & cntRowsSheet2)
Dim i As Long
For i = 1 To UBound(arrSource, 1) 'ubound gives you the upper bound of the array
rgTarget.Replace arrSource(i, 1), arrSource(i, 2)
Next
End With
End Sub
You could omit the whole "cntRows"-stuff by using currentregion - which returns the area around one cell that is surrounded by empty rows and columns (see https://learn.microsoft.com/en-us/office/vba/api/excel.range.currentregion).
That means that wsSource.Range("A1").CurrentRegionwill return all cells until the first empty row and until the first empty column - I assume this is exactly what your are looking for. The same for sheet2 as well.
To omit the first row, you can use offset:
set rgTarget = wsTarget.Range("A1").CurrentRegion.Offset(1)
The code then looks like
Option Explicit
Public Sub updateSheet2IDs()
Dim wsSource As Worksheet
Set wsSource = ThisWorkbook.Worksheets("Sheet1")
Dim wsTarget As Worksheet
Set wsTarget = ThisWorkbook.Worksheets("Sheet2")
'array includes both columns: arrsource(1,1) = A2, arrsource(1,2) = B2
Dim arrSource As Variant
arrSource = wsSource.Range("A1").CurrentRegion.Offset(1)
Dim rgTarget As Range
Set rgTarget = wsTarget.Range("A1").CurrentRegion.Offset(1)
Dim i As Long
For i = 1 To UBound(arrSource, 1) 'ubound gives you the upper bound of the array
rgTarget.Replace arrSource(i, 1), arrSource(i, 2)
Next
End Sub
I have a column in one sheet
I am trying to transfer it to another sheet on the same workbook. It must appear like the image below. The values must appear after the first ID column.
I tried the code below after reading and watching videos. I am further trying to identify the lastrow in Sheet2 and paste the values from Sheet1 to the next available row.
Sub Transpose()
Dim SourceSheet As Worksheet
Dim TransferSheet As Worksheet
Dim inRange, outRange As Range
Dim finalrow As Integer
Dim i As Integer
'Assign
Set SourceSheet = Sheets("Sheet1")
Set TransferSheet = Sheets("Sheet2")
SourceSheet.Activate
Set inRange = ActiveSheet.Range("B2:B11")
inRange.Copy
'TRANSFER
TransferSheet.Activate
finalrow = TransferSheet.Cells(Rows.Count, 1).End(xlUp).Row 'find last row
For i = 2 To 11
outRange = TransferSheet.Range(Cells(ii, finalrow), Cells(ii, finalrow))
outRange.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=True
Next i
End Sub
Here is an example following on from my comments
Option Explicit
Public Sub MyTranspose()
'This assumes column to row transpose
Dim SourceSheet As Worksheet, TransferSheet As Worksheet
Dim inRange As Range, inRangeValues() As Variant, outRangeValues() As Variant
Dim finalRow As Long
Set SourceSheet = ThisWorkbook.Worksheets("Sheet1") 'Assign reference
Set TransferSheet = ThisWorkbook.Worksheets("Sheet2")
Set inRange = SourceSheet.Range("B2:B11")
inRangeValues() = inRange.Value 'generate 2d array
outRangeValues = Application.Transpose(inRangeValues)
With TransferSheet 'Hold reference to parent worksheet
finalRow = .Cells(Rows.Count, 1).End(xlUp).Row 'find last row
If inRange.Columns.Count > 1 Then '2d array for output
.Cells(finalRow + 1, 2).Resize(UBound(outRangeValues, 1), UBound(outRangeValues, 2)) = outRangeValues 'Resize according to output array dimensions
Else '1D array for output
.Cells(finalRow + 1, 2).Resize(1, UBound(outRangeValues, 1)) = outRangeValues
End If
End With
End Sub
These were my comments (plus a bit):
Use Long rather than Integer, inRange needs to be explicitly declared as Range not implicitly as Variant. With Dim inRange, outRange As Range only outRange is a Range. You need Dim inRange As Range, outRange As Range.
You need Set when creating reference to Range object e.g. Set outRange = TransferSheet.Range(Cells(ii, finalrow), Cells(ii, finalrow)); here Cells will refer to currently active sheet and ii is never declared, but you are using a loop variable called i - typo? Other than that I am not sure pastespecial will work there either.
I would (depending on size of inRange as Transpose has a limit and will truncate or complain after that) read into array, use Transpose function and write out with Resize.
Use Worksheets collection not Sheets. Fully qualify cells references with parent sheet names; As you have these in variables just use the appopriate variable names. You don't need Activesheet and Activate this way and thus your code will be less bug prone (explicit sheet reference) and faster (due to not Activating sheet).
Give your sub a different name from the existing VBA method (something better, yet still descriptive, than I have used.
Try this, please:
Dim SourceSheet As Worksheet, TransferSheet As Worksheet
Dim rowVal As Variant, nrCol As Long, ColumnLetter As String
Set SourceSheet = ActiveWorkbook.Sheets("Sheet1")
Set TransferSheet = ActiveWorkbook.Sheets("Sheet2")
rowVal = SourceSheet.Range("B2:B11")
nrCol = UBound(rowVal)
ColumnLetter = Split(Cells(1, nrCol + 1).Address, "$")(1)
TransferSheet.Range("B2:" & ColumnLetter & 2) = Application.WorksheetFunction.Transpose(rowVal)
So, the code declares both pages as you did.
Then the range in B:B column is included in the rowVal array. The number of columns is defined like Ubound(rowVal) and the Column letter of the sheet to paste the values is determined like ColumnLetter. nrCol + 1 is used because the paste cell will be in B:B column (the second one) and counting does not start from the first column.
Then, using Transpose function the array content is pasted in the TransferSheet appropriate row. The range column is built using the previous determined ColumnLetter...
I'm trying to look for the last row of data between column A and I and then duplicate the value to the row below which is empty.
Every time I run it, Excel crashes
Sub insert_row()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
LastRow = LastRow
Dim lastrow_start As String
Dim lastrow_end As String
lastrow_start = "A" & LastRow
lastrow_end = "I" & LastRow
Dim lastrowregion As String
lastrowregion = lastrow_start & ":" & lastrow_end
Dim lastrowrange As Range
Set lastrowrange = Range(lastrowregion)
Dim rng As Range
Set rng = Range(lastrow_start)
Do While (rng.Value <> "")
rng.Offset(1).insert
lastrowrange.Copy rng.Offset(1)
Set lastrowrange = rng.Offset(2)
Loop
End Sub
Is it just copying too much and causing a crash? It's only nine columns and they're all text apart from one cell which is a shape (button).
You are trying to set a String to a range object. To get the range use:
Set rng = Range(lastrowregion)
The Range you are getting is A2:I2. So your Do While will error because rng.Value is actually returning an Array. You could either loop through either the Range or the Array at that point if you intended on it being multiple cells.
If the goal is simply to copy the last row of data down one row then this method can be much simpler. You can simply set the Offset to equal the value of the last row. Since they are the same size it will just work.
To show this I used CurrentRegion but you could also do it with your A2:I2 Range.
Public Sub copyLastRowDown()
Dim region As Range
Set region = ThisWorkbook.Worksheets("Sheet1").Range("A1").CurrentRegion
With region.Rows(region.Rows.Count)
.Offset(1).Value = .Value
End With
End Sub
Additional Notes
Use Option Explicit to ensure all variables are explicitly declared.
Declare and assign variables next to where they are going to be used, but place them in a reasonable place.
Do not use underscore case as this has special meaning with events and interfaces.
I need some advice for my code. I really appreciate if some members can edit my code. Thanks
My code below is looking for the name on column B and copy the result on another sheet if 2 conditions met:
- The row.value on column G = "ongoing"
- The row.value on column C = "HP"
When I run this code, got an error-message box "Range of Object"_Worksheet failed.
I am trying to change the set "mytable to ShSReturn.ListObject ("Survey Return")" with mytable as Range, another message error "Subscription out of range"
Sub LOf()
Dim cell As Variant
Dim myrange As Long, lastrow As Long, finalrow As Long, resultrow As Long
Dim mytable As Range
lastrow = ShSReturn.Range("G" & ShSReturn.Rows.Count).End(xlUp).Row
finalrow = ShSReturn.Range("C" & ShSReturn.Rows.Count).End(xlUp).Row
resultrow = ShSReturn.Range("B" & ShSReturn.Rows.Count).End(xlUp).Row
Set mytable = ShSReturn.ListObjects("Survey Return")
cell = 7
For Each cell In mytable
If mytable.Cells(cell, lastrow).Value = "Ongoing" _
And mytable.Cells(cell, finalrow).Value = "HP" Then
mytable.Cells(cell, resultrow).Copy
ShPPT.Cells(cell, 17).PasteSpecial xlPasteValues
resultrow = resultrow + 1
End If
Next cell
End Sub
I think there's some confusion about the nature of your ListObject, as specified in your original code (see comments to the question). When you select a bunch of cells and go to Insert -> Table, then as well as the table object, Excel defines a Range with the name of that table: a named Range. This Range may be referenced directly in VBA as such:
Set mytable = Range("Table1")
Note that Range names may not contain spaces
On the assumption that you have a named Range, it might be something like this:
Sub LOf()
Dim myrange As Long, lastrow As Long, finalrow As Long, resultrow As Long
Dim mytable As Range
lastrow = ShSReturn.Range("G" & ShSReturn.Rows.Count).End(xlUp).Row
finalrow = ShSReturn.Range("C" & ShSReturn.Rows.Count).End(xlUp).Row
resultrow = ShSReturn.Range("B" & ShSReturn.Rows.Count).End(xlUp).Row
Set mytable = ActiveSheet.Range("SurveyReturn") ' It's best to specify which sheet your source data is on. Presumably "ShSReturn" is the CodeName of your results sheet
Dim x As Long
For x = 7 To mytable.Cells(mytable.Cells.Count).Row ' Start at Row 7, and finish at the row number of the last cell in that Range
If mytable.Cells(x, **lastrow**).Value = "Ongoing" And mytable.Cells(x, **finalrow**).Value = "HP" Then
mytable.Cells(x, **resultrow**).Copy
ShPPT.Cells(cell, 17).PasteSpecial xlPasteValues
resultrow = resultrow + 1
End If
Next x
End Sub
Note that the above code will not work in its present form. What I have done is an approximation of what I think you're looking for: however you're going to have to do a bit of work, because the code in your question has some fundamental issues. For example, in your code you have lines like this:
mytable.Cells(cell, resultrow).Copy
However addressed cells within Ranges are in the format Range.Cells(Row, Column) - where Row and Column are numbers. However in your code resultrow as defined at the top is a Row, not a Column. You need to work out what exactly you want to copy, in terms of which row/column and re-write your code accordingly.
If you want to provide clarity, I'll be happy to edit my answer to accommodate what you want.
For the life of me, I can't figure out how to loop a formula over columns and down rows. I put some code together that I thought would be on the right track:
Sub LoopAcrossColsRows()
Dim C As Integer
Dim R As Integer
Dim LastCol As Integer
Dim LastRow As Long
Dim lr As Long
Worksheets("Sheet1").Activate
LastCol = Cells(1, Columns.Count).End(xlToLeft).Column
LastRow = Cells(Rows.Count, 20).End(xlUp).Row
lr = Cells(Rows.Count, 1).End(xlUp).Row
For C = 2 To LastCol
For R = lr + 3 To LastRow
.Cells(R, C).FormulaR1C1 = “=SUMPRODUCT(--(R6C:R7C>=RC1), --(R6C:R7C<=(RC1+30))*R4C)"
Next R
Next C
End Sub
The formula is causing an expected list separator or ) error. The formula works when I insert it on my worksheet, so I do not know what could be going wrong. Thanks for all help in advance.
As an aside, is there anyway to create an input box that would prompt for a new sheet name, which would be reflected as the new sheet name in the Worksheets("Sheet1").Activate line, so that I wouldn't have to keep changing the sheet name in the vba code?
Thanks again,
You need to qualify the Cells property with an object
Your error message from the VB interface is due to the fact that your first quote mark is a left double quote and not a regular quote mark.
Try
Cells(R, C).FormulaR1C1 = "=SUMPRODUCT(--(R6C:R7C>=RC1), --(R6C:R7C<=(RC1+30))*R4C)"
And better than looping would be a routine that assigns the formula to a multicell range object.
Not Tested:
range(cells(lr+3,2),cells(lastrow,lastcol)).FormulaR1C1 = "=SUMPRODUCT(--(R6C:R7C>=RC1), --(R6C:R7C<=(RC1+30))*R4C)"
The line
.Cells(R, C)...
should probably be qualified:
ActiveSheet.Cells(R,C)...
Better yet, you should retain a reference to the appropriate worksheet in a variable, instead of depending on it being the active sheet for the length of the For loop:
Dim wks As Worksheet
Set wks=Worksheets("Sheet1")
...
For R = lr + 3 To LastRow
wks.Cells(R, C).FormulaR1C1 = “=SUMPRODUCT(--(R6C:R7C>=RC1), --(R6C:R7C<=(RC1+30))*R4C)"
Next R
...
Your aside is an entirely separate question and doesn't belong here, but consider using the Excel Application.Inputbox function, or the VBA Inputbox function.
I believe you need the code to be
Activesheet.Cells(R,C)...
If you don't want to use name to call worksheets, then use index
worksheet(1).cells(x,y)...
however this is only useful if you never change the order of worksheet. Because worksheet(1) always goes to the first sheet of the workbook.
also you can set a variable as worksheet and use that
Dim ws1 As Worksheet
'or set it = Worksheet(1)
Set ws1 = Worksheets("sheet"1)
ws1.cells(1,1) = "test"
For the name problem you can try this
Dim yourName as String
yourName = Application.InputBox("Enter name of worksheet")
worksheet(1).Name = yourName
EDITED:
Ok, so took a detailed look at your code. There are a couple of changes to make here.
The loop for "R" doesn't get triggered because lr+3 will always > LastRow
C and R in "Cells(R,C)" could be problematic --> varR and varC.
.Cells --> Activesheet.Cells
You define your last row variable twice "LastRow" & "lr"? I'm not sure why. (also see #6 below.
RC reference in .FormulaR1C1 to be bound by []
The End(xl[Up, ToLeft]) method could miss the full size of the data set if it is not contiguous. "a1" can be replaced with different row/column ranges to constrain the variable. (Sorry for not knowing the source of this code, but have been using this for a while)
I'm not sure if the code will run seamless, or will loop exactly the range you need. But give it a go and see if it works. Hopefully the principle will guide you to a solution:
Sub LoopAcrossColsRows()
Dim varC As Integer
Dim varR As Integer
Dim LastCol As Integer
Dim LastRow As Long
Dim lr As Long
Worksheets("Sheet1").Activate
LastRow = Cells.Find("*", [a1], , , xlByRows, xlPrevious).Row
LastCol = Cells.Find("*", [a1], , , xlByColumns, xlPrevious).Column
For varC = 2 To LastCol
For varR = (LastRow + 3) To LastRow Step -1
Worksheets("Sheet1").Cells(varR, varC).FormulaR1C1 = "=SUMPRODUCT(--(R6C:R7C1>=RC1),--(R6C:R7C1<=(RC1+30))*R4C)"
Next varR
Next varC
End Sub