I have an issue with using a formula that contains a fixed cell in VBA. The issue comes when the row number of the variable in the new data changes.
The issue is explained using a simple example as follow. I hope you find it understandable.
Let's say I have a column of numbers (Time) and I want to multiply them by a variable in a cell (The cell below Variable in the following table, $A$2).
First result from first raw data:
The results in the table are calculated using the following formula "=R2C1*RC[-1]" in vba
Now in the next calculation, the row number and variable change and the part of the formula which is using a fixed cell cause problem.
Second raw data to be processed
Because it does not update the row number and use the old row number. I want it to find its location like the second part of the formula (B2 changes to B7).
Thank you for your help!
Cheers,
Aryan
you should reference the found cell row in your formula
ActiveCell.FormulaR1C1 = "=R" & ActiveCell.Row + 1 & "C1*RC[-1]"
but you should also avoid the Activate/ActiveXXX/Select/Selection pattern since is prone to have you quickly lose control over the actually active thing
finally you an use a loop to find all "Time" occurrences (see Here for more info about the pattern)
Option Explicit
Sub main()
Dim f As Range, firstCell As Range
With Worksheets("myWorksheetName") ' reference your worksheet (change myWorksheetName to your actual sheet name)
With .Range("B1", .Cells(.Rows.Count, "B").End(xlUp)) 'reference its column B cells from row 1 down to last not empty one
Set f = .Find("Time", LookIn:=xlValues, lookat:=xlWhole) 'search referenced range for first occurrence of "time"
If Not f Is Nothing Then ' if found...
Set firstCell = f ' store first occurrence cell
Do
f.Offset(1, 1).Resize(4).FormulaR1C1 = "=R" & f.Row + 1 & "C1*RC[-1]" ' populate the range one column to the right of found cell and 4 rows wide with the formula containg the reference of found cell row +1
Set f = .FindNext(f) ' serach for the next "Time" occurrence
Loop While f.Row <> firstCell.Row ' loop till you wrap back to initial occurrence
End If
End With
End With
End Sub
The notation R2C1 is an absolute reference to row 2, column 1.
If you want a reference that is relative to the current cell, you need to use relative reference notation.
RC[-1] points to a cell in the current row and one column to the left
R[1]C points to a cell one row down from the current cell and in the same column as the current cell.
Google for "R1C1 reference". You will find many articles, for e.g. https://smurfonspreadsheets.wordpress.com/2007/11/12/r1c1-notation/
Related
Need a little help here.
In the "Data" Tab I want to copy values in column "c2:c1000" and paste in column "a1" of another Tab.
This is what i have so far,
Dim x As Long
Dim lastRow As Long
lastRow = Worksheet("Data").Cells(3, Columns.Count).End(xlUp).Column
For x = 1 To lastRow
If Worksheets("Sheet2").Cells(2, "A") = "" Then
Worksheets("Data").Range("c2:c1000").Copy Destination:=Worksheets("Sheet2").Range(1, "A")
Sheets("Sheet2").Range("A1").Value = Format(Now, "mm/dd/yyyy HH:mm:ss")
Else
Worksheets("Data").Range("c2:c1000").Copy Destination:=Worksheets("Sheet2").Cells(2,
Columns.Count).End(xlToLeft).Offset(, 1)
'Sheets("Sheet2").Range("A1").Value = Format(Now, "mm/dd/yyyy HH:mm:ss") --> can't figure how to increment this as this will need to be on the subsequent empty column
End If
Next
End Sub
Your help will be greatly appreciated!
Thank you.
Pasting values first into range A1 and down and then next time to cell B1 and so on, leaves no space for the timestamp to A1, B1 etc. So, I assume that you would like to paste the random values to row 2. So cells A1, B1, ... are left for the timestamp.
Inside the With statements we can refer to properties of the wsAudit so we can replace the "Worksheets("Audit")." reference with just "."
The column.count expression just checks the amount of columns in the worksheet.
The expression .Cells(2, Columns.Count) just points to last cell in the row 2.
The .End(xlToLeft).Column then looks from this column to left and is supposed to find the last not empty cell on this row. It's basically the same idea that in Excel's sheet you would go to cell XDF2 and hit CTRL+Arrow Left from keyboard.
But instead of activating the cell we just want to get the columns index number and then add 1 (the new column) and save it into variable. Now the new column is known.
The expression Range(.Cells(2, newColAudit), .Cells(1000, newColAudit)).Value is really the same as e.g. Range("B2:B1000"), but with this we can use the row and column index numbers instead. This is useful as the column number varies.
And as Samuel pointed out the copy paste operation can be avoided by setting the areas equal.
Dim wsAudit As Worksheet
Dim newColAudit As Long
Set wsAudit = Worksheets("Audit")
With wsAudit
newColAudit = .Cells(2, Columns.Count).End(xlToLeft).Column + 1
Range(.Cells(2, newColAudit), .Cells(1000, newColAudit)).Value = Worksheets("Data").Range("C2:C1000").Value
.Cells(1, newColAudit).Value = Format(Now, "mm/dd/yyyy HH:mm:ss")
End With
Much like your LastRow* variable for your source sheet, create a LastColumn variable for your destination sheet, which will find the last used column the same way you are finding your last used row.
Like so:
Dim LastColumn As Long
LastColumn = Sheets("Audit").Cells(1, Columns.Count).End(xlToLeft).Column
Then use the variable like so:
Destination:= Worksheets("Audit").Cells(1, LastColumn)
It seems that your code contradicts your question too, in your question you explained the data will be written to the Audit sheet in row 1, using the next column each time but your code looks for values in row 2 in your If statement:
If Worksheets("Audit").Cells(2, "A") = "" Then is the same as If Worksheets("Audit").Range("A2") = "" Then.
If you mean to check the first row, change the 2 to 1.
To help improve your codes efficiency:
(Also see the link to 'how to avoid select' in that question):
You can achieve 'copy/paste' without actually using the 'copy' and 'paste' methods by assigning the value of one range to the other, as example, like so:
Worksheets("Audit").Cells(1, LastColumn).Resize(999, 1) = Worksheets("Data").Range("c2:c1000").Value
Note: Change the Resize Property rows to suit the source range (in this case you are wanting to move values from C2:C1000).
*The LastRow variable is a bit confusing, as it is looking for the last used column in row 3.
If it's meant to find a column, consider renaming it to avoid confusion later on in debugging.
If it's meant to find the last row, try like this:
LastRow = Worksheet("Data").Cells(Rows.Count, 1).End(xlUp).Row
I would like to find the last row in a range that I have selected so that my code is more dynamic and less likely to break if I exceed the range.
I am unsure of the syntax to use.
Instead of P4201, I would like to select the last row with a value within column P, whatever that may be.
Selection.AutoFill Destination:=Range("P2:P4201")
I am just unsure of the last syntax to select the last row with a value with P. Instead of doing P10000, I would like to make it cleaner.
The below macro counts up to the last filled cell in column P, and selects the full range from P2 until P-end.
Sub Select_Range()
Dim I As String
I = Sheet1.Range("P" & Rows.Count).End(xlUp).Row 'Determine last filled cell and count rows
Sheet1.Range("P2" & ":P" & I).Select 'select full filled range in column P.
End Sub
Let me know if this is what you're looking for.
I have a list of data with various different columns.
What I am trying to achieve is for excel to look through one of the columns and find the first value that occurs, copy that cell and then paste it in another cell. The data starts in row three (Rows 1 and 2 are headers). Let's say I want to paste the first value in G3.
Then, I need excel to find the next value that is different from the first in the same column and perform the same action as before: copy the value, and paste it in the next row in column G.
I have tried coding this but I'm not getting anywhere with it, and I haven't found a way to find a cell and then the next cell if the value within the cell is not defined (as the .Find function requires a value to search for). I know how to code the copy/paste functions but I cannot figure out how to get it to find the cell in the first place.
Any help would be much appreciated. Many thanks in advance.
In this sample, we are looking for values in column A:
Sub FillG()
Dim i As Long
i = Rows.Count
Range("A3:A" & i).Copy Range("G3:G" & i)
ActiveSheet.Range("G3:G" & i).RemoveDuplicates Columns:=1, Header:=xlNo
If Range("G3").Value = "" Then Range("G3").Delete Shift:=xlUp
End Sub
The method is to copy all of column A to G, then remove duplicates, then remove an empty in G3 if it exists.:
Hi guys this is my first post, I'm wondering if you can possibly assist me.
I'd like to write a macro / script that will allow me to put a formula into the column to the right of the currently selected one (for all active rows of the current column) based on what column I've selected. The issue I'm having is that I don't always know the current column letter (as my selection changes from worksheet to worksheet).
To give you an example:
One of my columns currently contains dates, that dates are entered in different formats though, some are separated with ".", some with "-", some with spaces and so on. I have a formula that will deal with this so I need to put this formula in the column to the right of the selected column (which has the dates).
I have been able to do this when I specify the column letter, but not if it changes.
Please can you help?
Give this a go,
Sub SomethingNeat()
Dim rng As Range, x
x = Selection.Column
On Error Resume Next
Set rng = Columns(x).SpecialCells(xlCellTypeConstants, 23)
If Not rng Is Nothing Then rng.Offset(, 1) = "'=MyFormula"
End Sub
You can use ActiveCell.Offset(0,1).Value = Variable
That means that whetever your current cell is you can move and "select" to put a value to the right cell of the one you have activated. You can move the selection using a loop.
Do
Workbooks("Yur workbook name").Worksheets(1).Range(Adress you want to start adding).Offset(0, 1).formula = "=FORMULA"
i = i + 1
ActiveCell.Offset(1, 0).Activate
Loop While i <= max_row
Edit: 2nd
Put the formula in a cell lets say C1
'Select a range
Set take = Worksheets(1).Range("C1")
take.Copy 'copy the formula
Worksheets(1).Paste Destination:=Worksheets(1).Range("B1:B10")
That will copy your function whenever you want it to
I have this code:
Sub Button26_Click()
Dim s1, s2
Set s1 = Worksheets("Invoice Generator")
Set s2 = Worksheets("Past Invoices")
With s2.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).EntireRow
.Cells(, "a").Value = s1.Range("f27").Value
End With
End Sub
To copy the cell f27 (initially) from sheet one to sheet two (on a new row each time) when the button is clicked. However, if I change "a" to any other cell reference then the data only gets copied once - subsequent clicks do not work.
Anyone have any ideas? Thanks.
You are setting the position by starting at the bottom of column A and looking up to the first non-blank cell then offsetting down one row to a blank cell. If you take this position but stuff values into the row starting at column B, you never fill that empty cell in column A so subsequent calls to the same routine will reposition to exactly the same spot. If you are going to use column B as the target, you need to position the new transfer of values based on the first blank cell in columns B, not column A.
Dim targetCOL as long
targetCOL = 2
With s2.Cells(Rows.Count, targetCOL).End(xlUp).Offset(1, 0).EntireRow
.Cells(, targetCOL).Value = s1.Range("f27").Value
End With
Since you have to change the column of both the positioning method and the target of the values it makes sense to put either the alphabetic columns reference or the numerical column index into a variable so that changing it there will change it in both required places. In the above suggested modification, I've used the column's numerical index to set column B.