IF conditional formula in VBA - Run-time error '1004' [duplicate] - vba

This question already has an answer here:
Different languages issue when inserting formula from VBA
(1 answer)
Closed 1 year ago.
I am getting a Run-time error '1004' at the line with IF statement. Checked the previous posts, but could not find a relevant solution.
My code is
Sub RefreshFormulae()
Set sh = ActiveSheet
lastR = sh.Range("B" & sh.Rows.Count).End(xlUp).Row
sh.Range("$J$3:$K$" & lastR).ClearContents
sh.Range("$H$3:$H$" & lastR).Formula = "=$G3"
sh.Range("$K$3:$K$" & lastR).Formula = "=IF($I3<>""Not Found"";$I3;"""")"
End Sub
Error is in the line below. Am I missing anything here?
sh.Range("$K$3:$K$" & lastR).Formula = "=IF($I3<>""Not Found"";$I3;"""")"

You need to use a comma (,) instead of a semicolon (;) in VBA, as the regional format for separators inside functions only works in the Excel formula bar interface, not macros/VBA.
Try:
"=IF($I3<>" & Chr(34) & "Not Found"",$I3,"""")"
This would make the whole block change to:
Sub RefreshFormulae()
Set sh = ActiveSheet
lastR = sh.Range("B" & sh.Rows.Count).End(xlUp).Row
sh.Range("$J$3:$K$" & lastR).ClearContents
sh.Range("$H$3:$H$" & lastR).Formula = "=$G3"
sh.Range("$K$3:$K$" & lastR).Formula = "=IF($I3<>" & Chr(34) & "Not Found"",$I3,"""")"
End Sub

You need to use commas when entering formulas in VBA, even when you use semicolon directly in the sheet.
Sub RefreshFormulae()
Set sh = ActiveSheet
lastR = sh.Range("B" & sh.Rows.Count).End(xlUp).Row
sh.Range("$J$3:$K$" & lastR).ClearContents
sh.Range("$H$3:$H$" & lastR).Formula = "=$G3"
sh.Range("$K$3:$K$" & lastR).Formula = "=IF($I3<>""Not Found"",$I3,"""")"
End Sub

Related

Filling cells with loop in VBA Excel [duplicate]

This question already has an answer here:
Using VBA to place multiple formulas in one cell
(1 answer)
Closed 6 years ago.
I'm trying to fill cells with a for loop like this:
For i = 1 To Target
Range("C" & i & ":C" & i ).Formula = "='Sheet1'!A" & i & "/" & "'Sheet2'!B" & i"
Next i
And I want to see that in the formula bar:
='Sheet1'!A1 & "/" & 'Sheet2'!B1
='Sheet1'!A2 & "/" & 'Sheet2'!B2
...
Unfortunately it's not working. If I try only the first part like that:
For i = 1 To Target
Range("C" & i & ":C" & i ).Formula = "='Sheet1'!A" & i
Next i
This code results this fine, but this is not enough for me:
='Sheet1'!A1
='Sheet1'!B1
...
What is wrong with my frist code?
You could try
Range("C" & i & ":C" & i).FormulaR1C1 = "=Sheet1!RC1 & ""/"" & Sheet2!RC2"
If you are having trouble with double quotes in a concatenated string, I sugest you remove as many as you can. Single characters can be referred to by their ASCII code number with the Chr function. The / character is 47.
Dim i As Long, target As Long
With ActiveSheet
target = .Cells(Rows.Count, "A").End(xlUp).Row
For i = 1 To target
.Range("C" & i).Formula = "='Sheet1'!A" & i & Chr(47) & "'Sheet2'!B" & i
Next i
End With
You do not have to increment through a range is the formula is constructed properly. Either an xlA1 or xlR1C1 reference style can be used (see xlReferenceStyle enumeration).
Dim i As Long, target As Long
With ActiveSheet
target = .Cells(Rows.Count, "A").End(xlUp).Row
'xlA1 style
.Range("C1:C" & target).Formula = "='Sheet1'!A1/'Sheet2'!B1"
'xlR1C1 Style
.Range("C1:C" & target).FormulaR1C1 = "='Sheet1'!RC1/'Sheet2'!RC2"
End With
Note that an xlR1C1 style requires the Range.FormulaR1C1 instead of the Range.Formula.

Correct formula giving "Run-time error '1004': Application-defined or object-defined" error when running sub

I want to paste formula's in column B with a delay in between. The formula should only paste the formula if the cell left to it (in case of B1 this is A1) is not empty, like this:
I have the following VBA that pastes the formula with a delay of 1 sec.
Option Explicit
Sub RetrieveDataDelay()
'paste formulas into cells, then calculate, then delay rinse repeat
'=========================
'Declare Variables
'=========================
Dim i As Long 'used to loop
Dim rowCount As Long
Dim LastRow As Long 'used to find the last row
'=========================
'Setup for Speed
'=========================
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'=========================
'Define Variables
'=========================
LastRow = Worksheets(ActiveSheet.Name).Cells(Rows.Count, 1).End(xlUp).Row 'This will find the last used row in column A, change the number 1 to whatever column number you want
rowCount = 1 ' set to how many rows you want to do at a time
'=========================
'Do Work
'=========================
For i = 1 To LastRow Step rowCount
Range("B" & i, "B" & WorksheetFunction.Min(i + rowCount - 1, LastRow)).Formula = "'=IF(ISBLANK(A" & i & ");" & """" & """" & ";Dump(Volumes(A" & i & ";2528;1010;TRUE;" & "Volume" & ";TRUE)))" 'set the formula to whatever it needs to be
Calculate
Application.Wait (Now + TimeValue("00:00:01")) 'this delays 1 second
Next i
'=========================
'Setup for Speed - Reverse
'=========================
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
The error occurs at this part
Range("B" & i, "B" & WorksheetFunction.Min(i + rowCount - 1, LastRow)).Formula = "=IF(ISBLANK(A" & i & ");" & """" & """" & ";Dump(Volumes(A" & i & ";2528;1010;TRUE;" & "Volume" & ";TRUE)))"
The errors has something to due with the formula, which it does not accept. This is a custom formula used with an excel extension. I know that the formula works, as I put a single quotation mark in front as such:
Range("B" & i, "B" & WorksheetFunction.Min(i + rowCount - 1, LastRow)).Formula = "'=IF(ISBLANK(A" & i & ");" & """" & """" & ";Dump(Volumes(A" & i & ";2528;1010;TRUE;" & "Volume" & ";TRUE)))"
so that it pastes the formally literally. If I then remove the quotation mark from the formula the formula works. So the questions remains why it doesn't accept this specific formula in the VBA.
Using Excel 2013.
Range.Formula needs the formula notation always in en_us format independent of the current locale settings. That means functions in English language and also comma as delimiter between formula parameters and not semicolon.
So
.Formula = "=IF(ISBLANK(A" & i & ")," & """" & """" & ",Dump(Volumes(A" & i & ",2528,1010,TRUE," & "Volume" & ",TRUE)))"
After setting Range.Formula, the locale settings will be nevertheless active in the sheet. So in the sheet the formula will be delimited with semicolons if so set.
Btw.: The complicated string notation for double quotes is not neccessary.
.Formula = "=IF(ISBLANK(A" & i & "),"""",Dump(Volumes(A" & i & ",2528,1010,TRUE," & "Volume" & ",TRUE)))"

Overcoming the 255 char. limit for formulaArray in Excel VBA [duplicate]

This question already has an answer here:
How to automatically input an array formula as string with more than 255 characters in length into an excel cell using VBA?
(1 answer)
Closed 2 years ago.
I need an array to be inserted into a specific cell and I keep running into the 1004 error. Here is the code:
Range("o37").FormulaArray = "=CONCATENATE(SUM(IF(('2016 Summary'!$T$4:$T$39<=" & mon & ")*" & _
"('2016 Summary'!$D$4:$D$39>0)*('2016 Summary'!$T$4:$T$39+'2016 Summary'!$D$4:$D$39>" & mon & ")*" & _
"('2016 Summary'!$S$4:$S$39=TRUE),(1),IF(('2016 Summary'!$T$4:$T$39<=" & mon & ")*('2016 Summary'!$S$4:$S$39=TRUE)*" & _
"('2016 Summary'!$D$4:$D$39=0),(1),0)))&"" - Employees"")"
That array is supposed to be inserted into O37 with the "mon" variable based on the activecell column. The formula works when I manually put it in the sheet. I have tried splitting it with .replace and I get the same error. What can I do to make this work with formulaArray?
Depending upon how long the value in the mon variable is, it seems that your array formula is only marginally over the limit at ~290-310 characters and a large portion of that is comprised of the external worksheet name (e.g. '2016 Summary'). Changing the worksheet name temporarily to a single character is sufficient to bring the formula down to the ~190-210 character range; more than adequate to stuff the formula into the cell's Range.FormulaArray property.
Dim strWS As String, chrWS As String
strWS = "2016 Summary"
chrWS = Chr(167) '<~~ any unque, legal, single character that can be temporarily used as a worksheet name
With Worksheets(strWS)
.Name = Chr(167)
End With
With Worksheets("worksheet_with_array_formula")
.Range("o37").FormulaArray = "=CONCATENATE(SUM(IF((" & chrWS & "!$T$4:$T$39<=" & mon & ")*" & _
"(" & chrWS & "!$D$4:$D$39>0)*(" & chrWS & "!$T$4:$T$39+" & chrWS & "!$D$4:$D$39>" & mon & ")*" & _
"(" & chrWS & "!$S$4:$S$39=TRUE),(1),IF((" & chrWS & "!$T$4:$T$39<=" & mon & ")*(" & chrWS & "!$S$4:$S$39=TRUE)*" & _
"(" & chrWS & "!$D$4:$D$39=0),(1),0)))&"" - Employees"")"
End With
With Worksheets(chrWS)
.Name = strWS
End With
Excel will change the worksheet name within the formula, adding wrapping ticks (e.g. '2016 Summary'!$T$4:$T$39) to compensate for the space in the new (old) worksheet name.
If you provided more detail on the value of mon and some sample data from the '2016 Summary' worksheet, it also possible that the formula could be improved. At first glance, it looks like an array formula may not be absolutely necessary at all.
Sub SetTooLongArrayFormula(ByVal rn As Range, ByVal sFormula As String)
Dim sFormat As String
sFormat = rn.Cells(1, 1).NumberFormat
rn.FormulaArray = ""
rn.Cells(1, 1).NumberFormat = "#"
rn.Value = sFormula
rn.Cells(1, 1).NumberFormat = sFormat
rn.Select
DoEvents
SendKeys "{F2}", True
DoEvents
SendKeys "+^{ENTER}", True
End Sub
Sub Test()
'Do not run this macro from VBE Editor
'Run this macro from the Macros ribbon on the Developer tab with the Excel worksheet in front.
Dim sFormula As String
sFormula = "=""1"""
For i = 1 To 250
sFormula = sFormula & "&""1"""
Next
SetTooLongArrayFormula Range("A1:A2"), sFormula
End Sub

VBA: Dims inside a Vlookup

I'm trying to use a Vlookup with the line:
wRange.FormulaR1C1 = "=VLOOKUP(RC[-" & (vCol - 1) & "],'" & vSheet & "'!C[15]:C[16],2,FALSE"
where vCol as Integer = 9 and vSheet as String = 082015
So basically, that line can be rewritten as:
wRange.FormulaR1C1 = "=VLOOKUP(RC[-8],'082015'!C[15]:C[16],2,FALSE)"
However, I'm getting an "Application-defined or object-defined error" during run-time.
Do you see what I'm doing wrong?
Thanks.
"=VLOOKUP(RC[-" & (vCol - 1) & "],'" & vSheet & "'!C[15]:C[16],2,FALSE)"
Note missing bracket at the end

Using a passed string to access different class properties

Preface: I'm very much a dabbler at coding, and I'm not surprised my current code doesn't work, but I can't figure out whether I'm trying an approach that is fundamentally not possible or whether I just don't understand the correct syntax. Unlike the majority of the rest of my current project code, I haven't yet found the solution in the many other posts here.
Context: Playing Elite: Dangerous. I have a list of star systems in an Excel worksheet with each row containing one system (columns: name, x, y, z coordinates, and some properties such as Visited, RareGoodsSource). I've created a StarSystem class and read the worksheet into a Collection of StarSystems (named colSys). This works. For each property of the class I have a separate worksheet (columns: name, property) where I manually adjust property values (e.g. just visited Tau Ceti in game, on worksheet "csvVisited" manually add row "Tau Ceti", "TRUE"). In VBA I then compare those to the values in the Collection elements and update the latter if necessary. (Eventually I pump all this stuff to AutoCAD to visualise and plan travel routes.)
Issue: I currently have a separate Sub for each property, identical except for the name of the worksheet (e.g. "csvVisited" / "csvRareGoodsSource") and the references to access the property (e.g. colSys.Item(r.Value).Visited / colSys.Item(r.Value).RareGoodsSource). This works. But it seems Wrong from the perspectives of aesthetics, efficiency, and maintenance. Surely I should have only one Sub, which I pass Visited or RareGoodsSource as required?
My current code for this generic sub is at the end of the post, first I have an extremely abstracted version for clarity. My first attempt was to simply literally replace Visited with strProperty everywhere in the Sub, and pass Visited or RareGoodsSource to the Sub into that string variable.
This works fine for the worksheet reference, presumably because .Item() requires a string anyway. I am not entirely surprised it does not work for the property reference, because I'm passing a string variable in the hopes VBA understands this as an object property name, but I have been unable to find how one should do this. Hopefully it's just a result of my embarrassing lack of basic programming knowledge, and I just need some brackets or quotes or &'s somewhere.
Simplified example code, which works correctly (...except the bit that doesn't, obviously):
Sub TestVisited()
Call TestGeneric("Visited")
End Sub
Sub TestGeneric(strProperty As String)
Dim wsCSV As Worksheet
Set wsCSV = ActiveWorkbook.Worksheets.Item("csv" & strProperty)
'successfully sets wsCSV to Worksheets.Item("csvVisited"),
'presumably because .Item() expects a string anyway.
Dim r As Range
For Each r In wsCSV.Range(wsCSV.Cells(2, 1), wsCSV.Cells(4, 1))
Debug.Print "Explicitly coded: " & colSys.Item(r.Value).Visited
Debug.Print "Passed as string: " & colSys.Item(r.Value).strProperty
Next r
'The first Debug.Print works, the second does not:
'"Object doesn't support this property or method."
End Sub
The current real code for context:
(Note I've disabled the error trap on the .Contains replacement, because otherwise that would trap this problem instead.)
Sub UpdatePropertyFromWorksheetCSVProperty(strProperty As String)
'set the cell column/row positions in Worksheets.
Let celCSVDataColumn = 2
'prepare reference to Worksheet to read.
Dim wsCSV As Worksheet
Set wsCSV = ActiveWorkbook.Worksheets.Item("csv" & strProperty)
'prepare reference to Range to read.
Dim rngData As Range
Set rngData = wsCSV.Range(wsCSV.Cells(celFirstDataRow, celKeyColumn), wsCSV.Cells( _
wsCSV.Cells(wsCSV.Rows.Count, celKeyColumn).End(xlUp).Row _
, celKeyColumn)) ' middle segment finds the last occupied cell in column A and returns its row index.
'for each Worksheet row, compare the property value in the Worksheet to the value in the Collection Element,
'if different write the Worksheet value to the Collection Element, and flag the Element as ModifiedSinceRead.
Dim r As Range
For Each r In rngData
'check Sytem exists in the Collection.
'except VBA Collections don't have a .Contains method apparently.
'use error trapping instead.
'On Error GoTo ErrorHandler
'compare/copy Worksheet and Collection values.
If Not colSys.Item(r.Value).strProperty = r.Offset(0, celCSVDataColumn - 1).Value Then
On Error GoTo 0 'disables error trap again.
Let colSys.Item(r.Value).strProperty = r.Offset(0, celCSVDataColumn - 1).Value
Let colSys.Item(r.Value).xlsModifiedSinceRead = True
'DEBUG: test to immediate window
Debug.Print "System " & colSys.Item(r.Value).Name & " " & strProperty & " property changed to " & colSys.Item(r.Value).strProperty & "."
'
End If
ResumeNextSystem:
Next r
'DEBUG: test to immediate window
Debug.Print colSys(1).Name & vbTab & colSys(1).x & vbTab & colSys(1).RareGoodsSource & vbTab & colSys(1).RareGoodsChecked & vbTab & colSys(1).Visited & vbTab & colSys(1).xlsModifiedSinceRead
Debug.Print colSys(10160).Name & vbTab & colSys(10160).x & vbTab & colSys(10160).RareGoodsSource & vbTab & colSys(10160).RareGoodsChecked & vbTab & colSys(10160).Visited & vbTab & colSys(10160).xlsModifiedSinceRead
Debug.Print colSys("Lave").Name & vbTab & colSys("Lave").x & vbTab & colSys("Lave").RareGoodsSource & vbTab & colSys("Lave").RareGoodsChecked & vbTab & colSys("Lave").Visited & vbTab & colSys("Lave").xlsModifiedSinceRead
'
Exit Sub
ErrorHandler:
MsgBox ("Processing Worksheet " & wsCSV.Name & " error at system " & r.Value & ", skipping to next.")
'DEBUG: test to immediate window
Debug.Print "Processing Worksheet " & wsCSV.Name & " error at system " & r.Value & ", skipping to next."
'
Resume ResumeNextSystem
End Sub
Solution in real code:
'stays as-is:
Set wsCSV = ActiveWorkbook.Worksheets.Item("csv" & strProperty)
'Get old:
If Not colSys.Item(r.Value).strProperty = r.Offset(0, celCSVDataColumn - 1).Value Then
'new:
If Not CallByName(colSys.Item(r.Value), strProperty, VbGet) = r.Offset(0, celCSVDataColumn - 1).Value Then
'Let old:
Let colSys.Item(r.Value).strProperty = r.Offset(0, celCSVDataColumn - 1).Value
'new:
CallByName colSys.Item(r.Value), strProperty, VbLet, r.Offset(0, celCSVDataColumn - 1).Value
You can use CallByName built-in function to get the property.
v = CallByName(colSys.Item(r.Value), strProperty, vbGet)
This KB article explains it: https://support.microsoft.com/kb/186143