Stuck overcoming the 255 characters in FormulaArray - vba

I have spent a long time trying to figure this out ! I would appreciate it if someone could give it a second glance to see where is my mistake ! The formula does not seem to do the replacement
Here is my code
Sub remove()
Dim theFormulaPart1 As String
Dim theFormulaPart2 As String
theFormulaPart1 = "=IFERROR(INDEX(Curve!D:D,MATCH(1,(DATE(RIGHT(Census!$BY2,4),LEFT(Census!$BY2,2),MID(Census!$BY2,4,2))-DATE(RIGHT(Census!$BM2,4),LEFT(Census!$BM2,2),MID(Census!$BM2,4,2))=Curve!$A:$A)*(Census!$T2=Curve!$C:$C),0)),""X()"")"
theFormulaPart2 = "IFERROR(INDEX(Curve!D:D,MATCH(1,(DATE(Year(Census!$BY2),Month(Census!$BY2),Day(Census!$BY2))-DATE(Year(Census!$BM2),Month(Census!$BM2),Day(Census!$BM2))=Curve!$A:$A)*(Census!$T2=Curve!$C:$C),0)),"""")"
With ActiveSheet.Range("CD2")
.FormulaArray = theFormulaPart1
.Replace """X()"")", theFormulaPart2
End With

the error is simple:
.Replace """X()"")", theFormulaPart2
will not work cus the formula after will not be valid. you need:
.Replace """X()""", theFormulaPart2
;)

OK, thanks #Dirk its turn out 255 character not the issue
Second glances:
Formula too long, spit it to make it readable in the future
Use Replace function that directly process a string
No need to use FormulaArray if just one cell
Replaced string should be "X()" not "X()")
Here is the second glance code:
Sub remove()
Dim theFormulaPart1 As String
Dim theFormulaPart2 As String
theFormulaPart1 = "=IFERROR(INDEX(Curve!D:D,MATCH(1,(DATE(RIGHT(Census!$BY2,4),LEFT(Census!$BY2,2),MID(Census!$BY2,4,2))-DATE(RIGHT(Census!$BM2,4),LEFT(Census!$BM2,2),MID(Census!$BM2,4,2))=Curve!$A:$A)*(Census!$T2=Curve!$C:$C),0)),""X()"")"
theFormulaPart2 = "IFERROR(INDEX(Curve!D:D,MATCH(1,(DATE(Year(Census!$BY2),Month(Census!$BY2),Day(Census!$BY2))-DATE(Year(Census!$BM2),Month(Census!$BM2),Day(Census!$BM2))=Curve!$A:$A)*(Census!$T2=Curve!$C:$C),0)),"""")"
theFormulaPart1 = Replace(theFormulaPart1, """X()""", theFormulaPart2)
ActiveSheet.Range("CD2") = theFormulaPart1
End Sub
Another tips:
Avoid using ActiveSheet, how?

Related

Using Application.Caller inside personal Function

I'm trying to use Application.Caller inside a Function (code below), but Excel returns a #VALUE and the background color is not set.
The personal function is called from an Excel cell. The idea is to map RGB values to color display in a "synchronous" fashion (i.e. without having to press a button).
When I run the following function through the debugger and step just before the instruction vCaller.Interior.Color = RGB(rlev, glev, blev), I can manually set the background color to green by pasting the exact same instruction in the execution console. So I'm puzzled as to why Excel is failing but VBA isn't.
Any clue ?
Public Function RGB_print(rlev As Integer, glev As Integer, blev As Integer)
As String
Dim vCaller As Variant
Set vCaller = Application.Caller
If TypeName(vCaller) = "Range" Then
vCaller.Interior.Color = RGB(rlev, glev, blev)
End If
RGB_print = ""
End Function
I completely agree with the comment from #Rory - I'd never use this code in my own projects, but I wanted to see anyway....
If in a normal module you create this function:
Public Function RGB_print(rlev As Integer, glev As Integer, blev As Integer)
Application.Volatile
End Function
Then in your sheet add this code:
Private Sub Worksheet_Calculate()
Dim rFormula As Range
Dim vForm As Variant
Dim sArguments As String
Dim sFormula As String
Dim rgblev As Variant
Set rFormula = Sheet1.Cells.SpecialCells(xlCellTypeFormulas)
For Each vForm In rFormula
If InStr(vForm.FormulaLocal, "RGB_print") <> 0 Then
sFormula = vForm.FormulaLocal
sArguments = Mid(sFormula, InStr(sFormula, "(") + 1, InStr(sFormula, ")") - InStr(sFormula, "(") - 1)
rgblev = Split(sArguments, ",")
vForm.Interior.Color = RGB(Evaluate(rgblev(0)), Evaluate(rgblev(1)), Evaluate(rgblev(2)))
End If
Next vForm
End Sub
This worked for formula such as:
=RGB_print(255,0,255) and =RGB_print(A5,B5,C5)
But again, find another way - this code has so many pitfalls I'll probably lose 100 reputation just for posting it.
Ok, as an alternative to Darrent's very precise reply, I'm reposting Tim Williwam's comment : whether one may/should mix functions and macros is an important question and it has been discussed here. Bottom line is : you can but don't do it unless you know what you are doing and are prepared to face the consequences.

Error When Inserting Formula greater than 255 characters

I have previously used this method within my program to insert a formula which was a lot larger however I am receiving "run-time error code 1004 unable to set the FormulaArray property of the range class" with this one:
Sub TEST()
Dim QFormula As String
Dim QCountF As String
Dim QCountF2 As String
Dim WStart As String
Dim MoreDash As String
Dim ValueF As String
QFormula = "=IF($K3=4,IF(_Q_>0,1,_M_),IF($K3=2,IF(_Q_>0,1,IF(COLUMN(P3)-MATCH(_S_,$A$1:P$1,0)>=8,IF(_Q2_>0,1,_M_),_M_)),IF(_Q_>0,1,IFERROR(IF((COLUMN(P3)-MATCH(_S_,$A$1:P$1,0)+1)-_V_<=13,1,_M_),_M_))))"
QCountF = "COUNTA(OFFSET(INDIRECT(ADDRESS(ROW(P3),COLUMN(P3)-4)),0,1,1,3))"
QCountF2 = "COUNTA(OFFSET(INDIRECT(ADDRESS(ROW(P3),COLUMN(P3)-8)),0,1,1,3))"
WStart = """START"""
MoreDash = """-"""
ValueF = "VALUE(MATCH(1,INDIRECT(ADDRESS(ROW(P3),MATCH(_S_,$A$1:P$1,0))):O3,0))"
With ActiveCell
.FormulaArray = QFormula
.Replace "_Q_", QCountF
.Replace "_Q2_", QCountF2
.Replace "_S_", WStart
.Replace "_M_", MoreDash
.Replace "_V_", ValueF
End With
End Sub
I am at a loss as to where I have gone wrong this time and am hoping you can help.
Thanks in advance
Taken straight from msdn site:
If you use this property to enter an array formula, the formula must use the R1C1 reference style, not the A1 reference style (see the example).
Worksheets("Sheet1").Range("E1:E3").FormulaArray = "=Sum(R1C1:R3C3)"
Update edit:
As you stated in comments even though msdn states you have to use R1C1 reference the A1 reference works too.
The actual issue in your code is:
ValueF = "VALUE(MATCH(1,INDIRECT(ADDRESS(ROW(P3),MATCH(_S_,$A$1:P$1,0))):O3,0))"
you have another _S_ within the replacement code this never gets changed as its already past the _S_ .Replace.
Fix:
ValueF = "VALUE(MATCH(1,INDIRECT(ADDRESS(ROW(P3),MATCH(""START"",$A$1:P$1,0))):O3,0))"

Populate VBA Array with list of values in native code

Hoping there is a quick answer to this question....
I have an array and I want to populate it with a list of arguments.
Sub EasyArrayInput()
Dim myArr() as variant
myArr = ("string1", "string2", "string3")
End Sub
I am well aware of how to loop through and populate with a for/next or do/while, but it would be nice to be able to populate an array when the values wont change without using a hardcoded method.
Sub UsualMethodThatIDontWantToDo()
Dim myArr(1 to 3) as variant
myArr(1) = "string1"
myArr(2) = "string2"
myArr(3) = "string3"
End Sub
Is there anyway to do it in a method similar to the first code snippet? I would prefer to do it that way. I apologize if this question has been asked/answered, but I'm not quite sure what the method I am asking about is called.
Thanks in advance!
Edit: Solution
The code snippet below (from the link that chancea sent) will create an array that is a variant and exaclty what I wanted.
Sub EasyArrayInput()
Dim myArr() as variant
myArr = Array("string1", "string2", "string3")
End Sub
The next code snippet looks to be useful for if you only have strings and don't want to initialize a variant:
Sub EasyArrayInput()
Dim myArr() as String
myArr = Split("String1,String2,String3", ",")
End Sub
How about?
Sub EasyArrayInput()
Dim myArr() As Variant
myArr = Array("string1", "string2", "string3")
End Sub
Assuming you have some sort of numeric sequence, you can do something like this:
Dim myArray()
myArray = [TRANSPOSE(INDEX("string"&ROW(1:10),))]
but frankly I think a loop is clearer.

Convert range to comma delimited string

If I had a column like this:
Col1
abc
def
ghi
jkl
How can I convert it to a string like this?:
"abc,def,ghi,jkl"
You can use the Join() function to join all the elements of a 1 dimensional array with a delimiter.
The Transpose() function is used below to form the dimensional array (this approach works on a single column or row).
Sub Main()
Dim arr
arr = Join(Application.Transpose(Range("A2:A5").Value), ",")
MsgBox arr
End Sub
or as a UDF
Public Function Merge(r As Range) As String
Merge = Join(Application.Transpose(r.Value), ",")
End Function
Just in case you need heavier machinery use one of the solutions provided in the answer below. I had similar challenge for ranges containing milion of cells. In such cases JOIN will lead to crash.
Check the question here:
Turn Excel range into VBA string
I have tested all the approaches provided in the above link. Solutions based on function JOIN have slow performance, or even lead to crash.
Ordinary loop through all the cells is way faster than JOIN function. The sting builder in accepted answer is even faster. With string builder, the strings consisting of millions of cells are build in seconds. This is the solution I have end up with.
Double-transpose works for doing string join on single-row values. Thanks #user2140173 and #brettdj!
debug.print join(Application.Transpose(Application.Transpose(Range("A1:G1").Value)),",")
Public Function COLSASLIST(Rng As Range) As String
Dim tempStr1 As String
tempStr1 = Replace(Replace(Join(Application.Transpose(Application.Transpose(Rng.Value)), ","), ",,", ""), ",,", ",")
If Right(tempStr1, 1) = "," Then tempStr1 = Left(tempStr1, Len(tempStr1) - 1)
COLSASLIST = tempStr1
End Function
Public Function ROWSASLIST(Rng As Range) As String
Dim tempStr1 As String
tempStr1 = Replace(Replace(Join(Application.Transpose(Rng.Value), ","), ",,", ","), ",,", ",")
If Right(tempStr1, 1) = "," Then tempStr1 = Left(tempStr1, Len(tempStr1) - 1)
ROWSASLIST = tempStr1
End Function
Using the new dynamic worksheetfunction TextJoin() of Microsoft 365/Excel2019 (+/-Mac) and Excel for the Web you can build a udf with the following range arguments
(1) a column or
(2) a row or even
(3) a contiguous range input (e.g. "A2:C5")
The optional 2nd argument ExcludeBlanks allows to omit blank values.
The function result is a comma separated list (important for case (3): the reading order is row wise).
Function Rng2List(rng As Range, Optional ExcludeBlanks As Boolean = True) As String
Rng2List = WorksheetFunction.TextJoin(",", ExcludeBlanks, rng)
End Function
See help at Textjoin function

Why can't I pass a String variable into VBA's Range function?

I'm trying to dynamically transpose a Recordset over a Range in Excel using VBA. I can do the transposition succesfully when I give it a static range such as this:
Range("A1:C16").Select
Range("A1:C16").Value = Application.WorksheetFunction.Transpose(scores)
When I try to use an equivalent string and pass it in to the Range function, however, it fails. My variable trange when printed out is "A1:C16" (including the double quotes). The reason I need to pass it a string is because the string is derived from a length variable which could be any value.
This code below fails:
Dim trange As String
trange = """A1:C" & slength & """"
MsgBox (trange)
Range(trange).Select
Range(trange).Value = Application.WorksheetFunction.Transpose(scores)
Unfortunately, escaping double quotes in VBA is ugly and that's why my trange assignment expression looks strange but when I MsgBox it, it is in fact giving me the correct value.
When you use the code Range("A1:C16").Select, the quotes are not part of the string, but simply delineate it. Therefore, you don't need to insert quotes into the string you're creating by escaping them. The following test case works for me:
Dim trange As String
Dim slength As Integer
slength = 5
trange = "A2:C" & slength
MsgBox (trange)
Range(trange).Select
Range(trange).Value = 5
Using string concatenation to construct range addresses is a bad idea. It's messy and error-prone (as your example illustrates!).
Instead of Range("A1:C16"), you can say any of the following:
Range("A1").Resize(16, 3)
Cells(1, 1).Resize(16, 3)
Range(Cells(1, "A"), Cells(16, "C"))
Range(Cells(1, 1), Cells(16, 3))
There are probably more possibilities. The key is that none of them involve string concatenation.
Replace 16 by slength in any of the examples above to make your variable-size range.
Also, it's good practice specify what worksheet you're referring to. Instead of plain Range(anything), use e.g.
Sheet1.Range(anything)
Worksheets("Sheet1").Range(anything)
or even better,
With Sheet1
.Range(anything)
' other stuff on Sheet1
End With