Excel VBA - Passing a Dynamic Range to a Function - excel

I created a function in Excel VBA:
Function codeList(Criteria As String, LookupRange As Range, ValueRange As Range, delimiter As String)
It works great when I pass hard coded ranges to it like this:
ActiveCell.FormulaR1C1 = "=codelist(RC[2],R2C4:R84C4,R2C3:R84C3,"","")"
I would like the rows to be dynamic, so instead of saying C2:C84 or D2:D84 I would like to make the C84 / D84 vary based on the number of columns that have data.
I tried to compute the last row and concatenate the ranges to make them dynamic when I call the function, but I get a compile syntax error :
Dim lastRow As Long
lastRow = Range("C" & Rows.Count).End(xlUp).Row
ActiveCell.FormulaR1C1 = "=codelist(RC[2],"R2C4:R" & lastrow &"C4", "R2C3:R" & lastrow & "C3"),"","")"
Any suggestions on how to make this work?

When you want to include double quotes inside a string, you have to use two double quotes to escape them.
ActiveCell.FormulaR1C1 = "=codelist(RC[2],R2C4:R" & lastRow & "C4, R2C3:R" & lastRow & "C3,"""")"
The four double quotes near the end convert to two double quotes (the empty string your looking for).
This way is longer, but I tend to use offsets in formulas. I find it easier to read than trying to determine where the strings begin and end
Const DBLQTE As String = """"""
ActiveCell.FormulaR1C1 = "=codelist(RC[2]," & _
Range("D2").Resize(lastRow - 1).Address(, , xlR1C1) & _
"," & _
Range("C2").Resize(lastRow - 1).Address(, , xlR1C1) & _
"," & _
DBLQTE & _
")"
Update
For a comma delimeter
ActiveCell.FormulaR1C1 = "=codelist(RC[2],R2C4:R" & lastRow & "C4, R2C3:R" & lastRow & "C3,"","")"
and
Const COMMA As String = ""","""
ActiveCell.FormulaR1C1 = "=codelist(RC[2]," & _
Range("D2").Resize(lastRow - 1).Address(, , xlR1C1) & _
"," & _
Range("C2").Resize(lastRow - 1).Address(, , xlR1C1) & _
"," & _
COMMA & _
")"

Qualify the parent worksheet and resize according to the column's populated limit. Resize every associated range that needs to be the same size.
Function codeList(Criteria As String, LookupRange As Range, ValueRange As Range, delimiter As String)
with LookupRange.parent
set LookupRange = .range(LookupRange.cells(1), .cells(.rows.count, LookupRange.cells(LookupRange.cells.count).column).end(xlup))
set ValueRange = ValueRange.cells(1).resize(LookupRange.rows.count, LookupRange.columns.count)
end with
'all the rest of the code
end function
Never use ActiveSheet or ActiveCell in a worksheet's UDF. The originating cell where the formula is in is the Application.Caller.

Related

How to break up a long formula in VBA?

I have a macro that's adding a very long formula to one of the cells.
I'm wondering if there's a way to break this formula up in the VBA editor up to make it easier to view and edit for other users the road.
Here's the code:
Sheet3.Select
Dim lastrow As Long
Range("D2").Formula = "=SUM(IFERROR(VLOOKUP(E2,Scores[[Values]:[Score]],2,FALSE),0)+IFERROR(VLOOKUP(H2,Scores[[Values]:[Score]],2,FALSE),0)+IFERROR(VLOOKUP(I2,Scores[[Values]:[Score]],2,FALSE),0)+IFERROR(VLOOKUP(J2,Scores[[Values]:[Score]],2,FALSE),0)+IFERROR(VLOOKUP(K2,Scores[[Values]:[Score]],2,FALSE),0)+IFERROR(VLOOKUP(L2,Scores[[Values]:[Score]],2,FALSE),0)+IFERROR(VLOOKUP(M2,Scores[[Values]:[Score]],2,FALSE),0))"
Range("D2").AutoFill Destination:=Range("D2:D" & lastrow), Type:=xlFillDefault
It looks like this:
I'm trying to get it to look more like this:
A space and underscore didn't work.
I could add a carriage return but that just adds it to the formula, I'm trying to make it easier to view inside the VBA editor.
I've also tried yelling at it but that hasn't worked either.
I'm wondering if some kind of CONCAT might do it? I'm pretty new to VBA (this is someone else's work that I'm modifying) so I'm not too well versed in what options are available.
Appreciate any insights!
The simple, direct answer is to build your formula first, by itself. Below is an artificial and contrived example but it should show the main idea.
Clearly you might better find a different way to write that formula as it seems repetitive which might mean there are ways to improve it, but I thought to start with this basic answer to your question about what your were trying to do that wasn't working.
dim myFormula as string
myFormula = "=SUM("
myFormula = myFormula & "A2"
myFormula = myformula & ",B2"
myFormula = myFormula & ",C2"
myFormula = myFormula & ")"
Range("A3").Formula = myFormula
This will also work in VBA if you prefer to use line continuations:
Dim myFormula As String
myFormula = _
"=SUM(A2" _
& ",B2" _
& ",C2" _
& ")"
Range("A3").Formula = myFormula
'Split' a Long Formula
Option Explicit
Sub WriteFormula()
Const LastRow As Long = 20 ' just to make it compile
Dim Formula As String: Formula = "" _
& "=IFERROR(VLOOKUP(E2,Scores[[Values]:[Score]],2,FALSE),0)" & vbLf _
& "+IFERROR(VLOOKUP(H2,Scores[[Values]:[Score]],2,FALSE),0)" & vbLf _
& "+IFERROR(VLOOKUP(I2,Scores[[Values]:[Score]],2,FALSE),0)" & vbLf _
& "+IFERROR(VLOOKUP(J2,Scores[[Values]:[Score]],2,FALSE),0)" & vbLf _
& "+IFERROR(VLOOKUP(K2,Scores[[Values]:[Score]],2,FALSE),0)" & vbLf _
& "+IFERROR(VLOOKUP(L2,Scores[[Values]:[Score]],2,FALSE),0)" & vbLf _
& "+IFERROR(VLOOKUP(M2,Scores[[Values]:[Score]],2,FALSE),0)"
'Debug.Print Formula
Sheet3.Range("D2:D" & LastRow).Formula = Formula
End Sub
Result in the Formula Bar For Cell D2
=IFERROR(VLOOKUP(E2,Scores[[Values]:[Score]],2,FALSE),0)
+IFERROR(VLOOKUP(H2,Scores[[Values]:[Score]],2,FALSE),0)
+IFERROR(VLOOKUP(I2,Scores[[Values]:[Score]],2,FALSE),0)
+IFERROR(VLOOKUP(J2,Scores[[Values]:[Score]],2,FALSE),0)
+IFERROR(VLOOKUP(K2,Scores[[Values]:[Score]],2,FALSE),0)
+IFERROR(VLOOKUP(L2,Scores[[Values]:[Score]],2,FALSE),0)
+IFERROR(VLOOKUP(M2,Scores[[Values]:[Score]],2,FALSE),0)

Vlookup in filtered Range with Varaible Lookup Value, Variable Lookup Range VBA

I am trying to apply Vlookup on a filtered range with Variable Lookup Value(Changing according to the row number) and Variable Lookup Range(From a user browsed workbook). But, the formula bar after running the code shows the formula as :-
=IFERROR(VLOOKUP(#Sri Lanka15-#a_One-#Time Base Rent,'[C_Rent Report_25082020.xlsx]Sheet 1'!$J$1:$N$968,4,0)," ")
I am not sure where these "#" signs are coming from. The lookup value for this particular row is :-Sri Lanka15-a_One-Time Base Rent.
Below is the code:-
Dim LR As Long ' Defined as Last row in source file
Dim nlr As Long 'Defined as Last row in Macro Workbook where vlookup is applied
Dim Filename As String
Filename = Application.GetOpenFilename(FileFilter:="All Files(*.xls; *.xlsx; *.csv),*xls,*.xlsx, *csv", Title:="Select File To Be Opened")
Workbooks.Open Filename:=Filename
sourcefile = Dir(Filename)
With ActiveSheet
Range("A1:AQ" & nlr).AutoFilter Field:=25, Criteria1:="One-Time Base Rent"
For Each cell In Range("AA2:AA" & nlr).SpecialCells(xlCellTypeVisible)
lookupvalue = Cells(cell.Row, "Z").Value
cell.Formula = "=IFERROR(VLOOKUP(" & lookupvalue & ",'[" & sourcefile & "]Sheet 1'!$J$1:$N$" & LR & ",4,0),"" "")" ' The problem seems to be here in lookup value as rest are appearing as fine in formula
Next
End With
Since i need to apply subsequent filters after this. i would like to keep the lookup value as variable.
I have tried WorksheetFunction.Vlookup too, but i am not sure how to define the range from a file chosen by user in worksheetfunction
Any help is highly appreciated !!
Thanks
Please, try replacing of
cell.Formula = "=IFERROR(VLOOKUP(" & lookupvalue & ",'[" & sourcefile & "]Sheet 1'!$J$1:$N$" & LR & ",4,0),"" "")"
with
cell.Formula = "=IFERROR(VLOOKUP(" & cells(cell.Row, "Z").Address & ",'[" & sourceFile & "]Sheet 1'!$J$1:$N$" & lr & ",4,0),"" "")"

Loop Variable inside a Formula Function

I am looping thru a column in which i need to add one to the loop variable within the FORMULA
My problem is how to write the correct FORMULA to go to the next cell using the lrow variable
Attached is a snippet of my code
Dim LastRow As Long
For lrow = 1 To 20
If Worksheets("cars").Range("P" & lrow) = "1" Then
Worksheets("cars").Range("a" & lrow).Formula = _
"=RIGHT(h & lrow ,FIND(""."",h & lrow))"
How do i concatenate the lrow variable within the formula ?
I have also tried "=RIGHT("h" & lrow ,FIND(""."","h" & lrow))"
Sometimes using Replace() can avoid a lot of concatenation and quote-balancing:
Worksheets("cars").Cells(lrow, "A").Formula = _
Replace("=RIGHT(H<rw>,FIND(""."",H<rw>))", "<rw>", lrow)
You can build the string, inserting the variables where necessary.
Worksheets("cars").Range("a" & lrow).Formula = _
"=RIGHT(h" & lrow & ",FIND(""."",h" & lrow & "))"
Note the extra quotation marks and (ampersands) that I have placed to expose the variable from the rest of the string.

VBA Formula creation uses the name of the Variable rather than the value

Im trying to create a code which will allow me to pull the average of 6 rows from a sheet called 'Raw Data' and dump it into a cell in a different worksheet, and then pull the average of the next 6 rows from 'Raw Data' and so on.
E.G. average('RawData'! A1:A6) in a new sheet A1
then
average('Raw Data'! A7:A12) In new sheet A2
etc.
So far I have managed to make the code loop in a way that I want however Im having trouble writing the actual formula in new sheet A1 and A2.
so far I have tried:
Dim address13 As String
address13 = "'Raw Data'" & "!" & Cells(start_row, RPM1300).Address & ":" & _
Cells(end_row, RPM1300).Address
ActiveCell.Offset(0, -4).Select
'1300
ActiveCell.Formula = "=Average(""" & address13 & """)"
However this returns the correct formula but with "" around it - rendering it useless.
I have also tried:
Sheets("Raw Data").Select
Dim address9 As Range
Set address9 = Range(Cells(start_row, RPM900).Address(), Cells(end_row, RPM900).Address())
Sheets("New Sheet").Select
rCell.Activate
ActiveCell.Offset(0, -5).Select
ActiveCell.Formula = "=Average(address9)"
However this just returns the name of the variable address9 in the formula rather than the actual range.
Note that RPM1300, RPM900, start_row, end_row and rCell are all variables in order for the code to loop and paste into the correct places.
Any help would be greatly apreciated
Try replacing your line:
ActiveCell.Formula = "=Average(""" & address13 & """)"
With:
ActiveCell.Formula = "=AVERAGE(" & address13 & ")"
The reason: the variable address13 is already defined as a String, that's why you don't need the extra " inside the brackets.
Code (use your first method:)
Dim address13 As String
address13 = "'Raw Data'!" & Cells(start_row, RPM1300).Address & ":" & _
Cells(end_row, RPM1300).Address
ActiveCell.Offset(0, -4).Select
ActiveCell.Formula = "=AVERAGE(" & address13 & ")"
Note: Try avoid using Select and ActiveCell , instead use referenced Ranges and Worksheets.
For instance, let's say you start from Cell A1, and you want this formula to be in Cell A5, you can use:
Range("A1").Offset(4, 0).Formula = "=AVERAGE(" & address13 & ")"
It is probably a bug in your version of Excel. Instead of
ActiveCell.Formula = "=Average(""" & address13 & """)"
try using
ActiveCell.Formula = '=Average("' & address13 & '")'
(single quotes around strings with double quotes and using only 1 double quote then).
Instead of
ActiveCell.Formula = "=Average(""" & address13 & """)"
Try
ActiveCell.Formula = "=Average("& chr(34) & address13 & chr(34) & ")"
At least here chr(34) returns the quotes you want. This may be tweaked if needed. Just change the number inside the ( )
Try using this:
Sub CellValue()
Dim adr As String
Dim sht As String
sht = "'Raw Data'"
adr = "A1:A3"
ActiveCell.Formula = "=AVERAGE(" & sht & "!" & adr & ")"
End Sub
Hope it helps :)
This formula will give you the same result and you'd be able to autofill it by dragging the cell handle.
=AVERAGE(OFFSET('Raw Data'!$A$2,ROW(A1)*6-7,0,6,1))
Filling in both formulas
Sub FillFormulas()
Const BASE_FORMULA = "=AVERAGE('Raw Data'!#Address)"
Dim lastRow As Long, x As Long
Dim Formulas
With Worksheets("Raw Data")
lastRow = .Range("A" & .Rows.Count).End(xlUp).Row / 6
End With
ReDim Formulas(1 To lastRow, 1 To 1)
With Worksheets("New Sheet")
For x = 1 To lastRow
Formulas(x, 1) = Replace(BASE_FORMULA, "#Address", .Cells((x * 6) - 5, 1).Resize(6).Address)
Next
.Range("A1").Resize(lastRow).Formula = Formulas
.Range("C1").Resize(lastRow).Formula = "=AVERAGE(OFFSET('Raw Data'!$A$2,ROW(A1)*6-7,0,6,1))"
End With
End Sub

Runtime formula error 1004 swaformula

I am getting a formula error message: Run-time error 1004, for the second recurrence of a formula in the code; the code section in question as follows, for quick reference I pasted the two formulas just below, the first one runs OK, the second one gives the error that I cannot figure out.
ActiveWorkbook.Names.Add Name:="pivotsourceFGPO", RefersToR1C1:=swaFormula (no errors when running the macro)
ActiveWorkbook.Names.Add Name:="pivotsourceorderbase", RefersToR1C1:=swaFormula (FORMULA giving error)
Code: (partial)
swaFormula = "=OFFSET(" & _
ActiveWorkbook.Worksheets(ActiveSheet.Index).Name & "!R" & ActiveCell.Row & "C" & ActiveCell.Column & ",0,0,COUNTA(" & _
ActiveWorkbook.Worksheets(ActiveSheet.Index).Name & "!C" & ActiveCell.Column & ") -" & swarow & ",COUNTA(" & _
ActiveWorkbook.Worksheets(ActiveSheet.Index).Name & "!R" & ActiveCell.Row & ")-" & swacol & ")"
ActiveWorkbook.Names.Add Name:="pivotsourceFGPO", RefersToR1C1:=swaFormula
Dim PTCache As PivotCache
Dim pt As PivotTable
Dim WS As Worksheet
Dim pvtfld As PivotField
' define the range which serves as the data range
Set WS = Sheets.Add ' add new sheet to hold the pivot table
Set PTCache = ActiveWorkbook.PivotCaches.Add(SourceType:=xlDatabase, SourceData:="=pivotsourceFGPO")
' Create the pivot table
Set pt = PTCache.CreatePivotTable(TableDestination:=WS.Range("A1"), TableName:="FGPOPivot")
ActiveWorkbook.ShowPivotTableFieldList = True
pt.PivotFields("Key").Orientation = xlRowField
pt.PivotFields("Key").Position = 1
pt.PivotFields("MTL").Orientation = xlRowField
pt.PivotFields("MTL").Position = 2
pt.PivotFields("Size Code").Orientation = xlRowField
pt.PivotFields("Size Code").Position = 3
pt.PivotFields("Week").Orientation = xlColumnField
pt.PivotFields("Week").Position = 1
pt.AddDataField pt.PivotFields("Wip Qty"), "Sum of Wip Qty", xlSum
'Order base 1 pivot
Sheets("Order base (1)").Select
Range("A1").Select
swarow = ActiveCell.Row - 1
swacol = ActiveCell.Column - 1
' make it comma and not colon
swaFormula = "=OFFSET(" & _
ActiveWorkbook.Worksheets(ActiveSheet.Index).Name & "!R" & ActiveCell.Row & "C" & ActiveCell.Column & ",0,0,COUNTA(" & _
ActiveWorkbook.Worksheets(ActiveSheet.Index).Name & "!C" & ActiveCell.Column & ") -" & swarow & ",COUNTA(" & _
ActiveWorkbook.Worksheets(ActiveSheet.Index).Name & "!R" & ActiveCell.Row & ")-" & swacol & ")"
ActiveWorkbook.Names.Add Name:="pivotsourceorderbase", RefersToR1C1:=swaFormula (FORMULA giving error)
You are relying on the ActiveCell property. There are better ways¹ to reference cells than that but if those 'best practises can be disregarded for a minute, the ActiveCell knows what its .Parent worksheet and workbook are.
Additionally, with no actual worksheet names provided, there is no guarantee that the ActiveSheet's .Name property does not contain a space and may require ticks (e.g. ') to wrap it. Either those should be added as a matter of course or the Range.Address property should be used to generate the strings for the cell range(s).
The non-volatile INDEX function could concevably replace the volatile² OFFSET function but I don't know enough about the layout of your data to suggest an infallible substitute formula.
Dim swaFormula As String, rngName As String
Dim swaRow As Long, swaCol As Long
rngName = "pivotSourceOrderBase"
swaRow = 0: swaCol = 0
With ActiveCell
swaFormula = "=OFFSET(" & _
.Cells(1).Address(external:=True, ReferenceStyle:=xlR1C1) & _
", 0, 0, COUNTA(" & _
.Cells(1).EntireColumn.Address(external:=True, ReferenceStyle:=xlR1C1) & _
")-" & swaRow & ", COUNTA(" & _
.Cells(1).EntireRow.Address(external:=True, ReferenceStyle:=xlR1C1) & _
")-" & swaCol & ")"
Debug.Print swaFormula '<~~ check the formula in the Immediate window
On Error Resume Next 'may be needed for the next line
.Parent.Parent.Names(rngName).Delete
On Error GoTo 0
.Parent.Names.Add Name:=rngName, RefersToR1C1:=swaFormula
End With
Walk through this and check the formula in the VBE's Immediate window before attempting to apply it to the named range creation.
¹ See How to avoid using Select in Excel VBA macros for more methods on getting away from relying on select and activate to accomplish your goals.
² Volatile functions recalculate whenever anything in the entire workbook changes, not just when something that affects their outcome changes. Examples of volatile functions are INDIRECT, OFFSET, TODAY, NOW, RAND and RANDBETWEEN. Some sub-functions of the CELL and INFO worksheet functions will make them volatile as well.

Resources