Evaluate long string (>255 characters) as array - excel

So I quite often find myself doing tasks on Excel which involve evaluating a text string as an array. Generally speaking I just use this:
Function EVAL(Ref As String)
EVAL = Evaluate(Ref)
End Function
So the formula will be, for example:
=EVAL("{"&CHAR(34)&SUBSTITUTE(TEXTJOIN(";",TRUE,MID(Index[Industries],2,LEN(Index[Industries])-2)),";",CHAR(34)&";"&CHAR(34))&CHAR(34)&"}")
The cells in this example will have contents like:
;Automotive;Rail;Energy;
;Automotive;Rail;
;Energy;
;Automotive;Aerospace;
(As it happens this is the precise problem I'm stuck on right now, though it has come up in different ways in the past.)
This has worked for me in the past, but I've been running into difficulties lately.
I have come to the conclusion it isn't working because application.evaluate, it turns out, has a character limit of 255. I've seen examples of VBA tricks to bypass this for text strings that are formulas rather than arrays, but copy-pasting those they don't seem to work for when I'm using it to interpret a text string as an array rather than as a formula.
Is there some trick to get this to work? (Or, indeed, is there some alternative method to achieve this altogether?)

Right, as per my comments, if you are using ms365, you could avoid your workbook to be xlsm just because you need to split values into an array. Make use of what is available with native functions, for example:
Formula in C2:
=TEXTSPLIT(CONCAT(A1:A4),,";",1)
Formula in D2:
=FILTERXML("<t><s>"&SUBSTITUTE(CONCAT(A1:A4),";","</s><s>")&"</s></t>","//s[node()]")
Note 1: As per time of writing you'd need to enable the BETA-channel to gain access to TEXTSPLIT(), and if I recall correctly your version (2203) is allowed to start using this function. Just google how to get access and update your Excel.
Both options can obviously be nested inside the UNIQUE() function.
Note 2: If at any point CONCAT()'s limits are reached (32767 characters, thanks #ScottCraner), maybe you can avoid using that with help of the lambda's helper function REDUCE():
=TEXTSPLIT(REDUCE("",A1:A4,LAMBDA(a,b,a&b)),,";",1)
Note 3: In case you can't update your Excel just yet, and you wonder how to use FILTERXML(), don't mind me refering you to another post I wrote a while back here.

Related

Excel's LAMBDA with a "kind of" composite function

Ever since I learnt that Excel is now Turing-complete, I understood that I can now "program" Excel using exclusively formulas, therefore excluding any use of VBA whatsoever.
I do not know if my conclusion is right or wrong. In reality, I do not mind.
However, to my satisfaction, I have been able to "program" the two most basic structures of program flow inside formulas: 1- branching the control flow (using an IF function has no secrets in excel) and 2- loops (FOR, WHILE, UNTIL loops).
Let me explain a little more in detail my findings. (Remark: because I am using a Spanish version of Excel 365, the field separator in formulas is the semicolon (";") instead of the comma (",").
A- Acumulator in a FOR loop
B- Factorial (using product)
C- WHILE loop
D-UNTIL loop
E- The notion of INTERNAL/EXTERNAL SCOPE
And now, the time of my question has arrived:
I want to use a formula that is really an array of formulas
I want to use an accumulator for the first number in the "tuple" whereas I want a factorial for the second number in the tuple. And all this using a single excel formula. I think I am not very far away from succeeding.
The REDUCE function accepts a LET function that contains 2 LAMBDAS instead of a single LAMBDA function. Until here, everything is perfect. However, the LET function seems to return only a "single" function instead of a tuple of functions
I can return (in the picture) function "x" or function "y" but not the tuple (x,y).
I have tried to use HSTACK(x,y), but it does not seem to work.
I am aware that this is a complex question, but I've done my best to make myself understood.
Can anybody give me any clues as to how I could solve my problem?
Very nice question.
I noticed that in your attempts you have given REDUCE() a single constant value in the 1st parameter. Funny enough, the documentation nowhere states you can't give values in array-format. Hence you could use the 1st parameter to give all the constants in (your case; horizontal) array-format, and while you loop through the array of the 2nd parameter you can apply the different types of logic using CHOOSE():
=REDUCE({0,1},SEQUENCE(5),LAMBDA(a,b,CHOOSE({1,2},a+b,a*b)))
This way you have a single REDUCE() function which internal processes will update the given constants from the 1st parameter in array-form. You can now start stacking multiple functions horizontally and input an array of constants, for example:
=REDUCE({0,1,100},SEQUENCE(5),LAMBDA(a,b,CHOOSE({1,2,3},a+b,a*b,a/b)))
I suppose you'd have to use {0\1} and {1\2} like I'd have to in my Dutch version of Excel.
Given your accumulator:
Formula in A1:
=REDUCE(F1:G1,SEQUENCE(F3),LAMBDA(a,b,CHOOSE({1,2},a+b,a*b)))

Basic IF Statement question -probably right in front of me

Sorry i know this is super basic but i didn't know where else to ask and i really feel like the answer is right in front of me...
I have a spreadsheet which im going to use to log PAT test results. When i select the test type from a drop down it changes the standards and thresholds in the bit below and will tell me if each test passes or fails. It uses several vlookups and relative references - so far no VBA. Looking at this photo. What I'm trying to do is get the formula in cell I13 to read the symbol in F13 and use that in the formula rather than typing the symbol directly into the formula as its going to change when i change the Class Type option.
So far ive gotten to this (has a blank IF to start with to keep it neat:
=IF(H13="","",IF((H13&F13&(VALUE(G13))),"PASS","FAIL"))
The bit in bold is where the issue is. When i run the evaluate formula it boils the bold bit down to "0.01>2" which is correct however it then wont read that in the larger IF statement - i think its the quotation marks. So i think it needs another function to allow the IF statement to read that as the logical test rather than a text string.
I've tried VALUE, FORMULATEXT, NUMBERTEXT, all the ones that might be close to what I'm trying to do but 100% stumped now. Always bring sup the #VALUE Error.
Appreciate any advice? TIA
There is no built-in function for that. You need either a VBA function or the old EVALUATE XLM function (which you can't use directly in a cell, it has to be in a defined name). Sample UDF:
Function EvaluateFormulaString(FormulaString as string)
EvaluateFormulaString = application.evaluate(formulastring)
End Function
then your formula would become:
=IF(H13="","",IF(EvaluateFormulaString(H13&F13&(VALUE(G13))),"PASS","FAIL"))

Excel - Row-wise calculation without helpers

Recently I answered a question about how to retrieve the MEDIAN() of each MEDIAN() in a 2-column matrix without helpers, e.g:
The row-wise calculation without helpers wasn't too hard because the median with only two values is always the average. Therefor a simple formula was all it took:
=MEDIAN((A1:A3+B1:B3)/2)
But for curiosity sakes however, wat if I would have at least a 3-column matrix?
The median will actually need to be calculated. Here the medians are {8,2,2}.
I can't seem to find a way to to get a row-wise calculation for 3+columns. In this case it's about MEDIAN() but I can imagine there could be other functionalities. Since this could be simplified data I don't want to resort to something like =MEDIAN(MEDIAN(A1:C1),MEDIAN(......
I tried to fiddle around with OFFSET(), though not a fan of volatile functions I was hoping it would either work directly with an array, or would be triggered correctly through using MEDIAN(LET(X,SEQUENCE(ROWS(A1:A3)),MEDIAN(OFFSET(A1:C1,X-1,0)))). I then moved on to combinations of either MMULT() or LARGE(), however none of my attempt were succesfull.
Question
So the question ultimately is; how do we return the result (array) of an row-wise calculation without helpers. And if not possible, that's also a perfectly fine answer so I can rest my head =)
New Answer
With the new BYROW() function one could use:
=MEDIAN(BYROW(A1:C3,LAMBDA(a,MEDIAN(a))))
The nested LAMBDA() in the 2nd parameter makes it a piece o' cake to loop all rows in a dynamic array (not a range per se).
Previous Answer (Pre-BYROW())
So. After a long thought, as far as my understanding goes this is not possible through current formulae. However, currently in BETA, Excel365 will feature the new LAMBDA() function which makes it possible to create your own function without VBA and even recursively call itself. It isn't the prettiest of solutions but I thought I would share what I did here:
Formula in E3:
=MED(A1:C3,"",ROWS(A1:A3))
Where MED() is our own LAMBDA() function created at the "name manager" menu. It reads:
=LAMBDA(rng,txt,rws,IF(rws=0,MEDIAN(FILTERXML("<t><s>"&txt&"</s></t>","//s")),MED(rng,TEXTJOIN("</s><s>",,txt,MEDIAN(INDEX(rng,rws;0))),rws-1)))
As can be seen there are 4 main parameters of which 3 variables:
rng - The range to be examined.
txt - A reserved variable to be used in FILTERXML().
rws - A counter.
The 4th parameter is a nested IF() which if the counter is as 0 will return the median of all medians. This is done through FILTERXML() which I will not get into detail right now.
If the counter is not yet at 0 it will recursively call the LAMBDA() function untill it is, and what it does is using the same three parameters but we can alter them right there and then. Therefor we leave rng intact, we concatenate the MEDIAN() of the row (current counter) through TEXTJOIN() to create a valid xml construct. And last but not least we need to lower the counter.
It's a struggle, but with LAMBDA() it will now be possible to do a rowwise calculation.
Note, if you are interested in the FILTERXML() construct, you might like this post where I now also included a LAMBDA() version of a SPLIT() function.

VBA loop preference

I am currently trying to optimize a VBA code with extreme execution time (56 hours and more on high end PC). The main problem is nested loops (up to 8) due to complicated iterative calculations and referencing ranges in formulas.
I have two questions:
1)
What loops are most preferred in VBA (based on their hardware strain for lack of better word)
2)
Is there a way to reference variable range of cells in a formula? For example in formula:
=AGGREGATE(15,6,SQRT((R2C10:R500000C10-RC[10])^2+(R2C11:R500000C11-RC[11])^2+(R2C12:R500000C12-RC[12])^2),1)
I am referencing relative cell: "R500000C10". There are two main issues: For one, the half milion cells might not capture all data - though in most cases there is less active cells than that, in same cases that might be an issue. Second, it takes forever to scan through so many cells (same reference is used in multiple of VLOOKUPs).
The first issue could be solved by referencing the whole column, but that makes the second problem worst.
I would like to reference it in a similar way that xlDown works:
Range().End(xlDown).Select
The problem there is I would need to reference it within the Active.Cell.FormulaR1C1. Is there a way to do that?
Thanks!
Well you wrote that you have a code with 8 loops and try to optimize it maybe you can edit and put part of that code, because your description is very general, however, with information that you put i speculate that about your point
1)it depend what do you want to do? because each "loop" have their own use for specifyc scenaries
2)there are tricks to do that but all depend what are you trying to do and why you want to use .formulaR1C1
again im speculating but you can try tu separate your loops in indenpendent function, and maybe you are putting a formula inside cells, and question is why? you can create functoins and Sub in your VBA code and do your "extrem execution" without use xtra "resours"

Renaming CUBEVALUE function to something shorter?

I've been using a rather long embedded CUBEVALUE() function, which is a pain to work with. It looks something like:
=IFERROR(VALUE(CUBEVALUE(arg1;arg2;arg3));CUBEVALUE(arg1;arg2;arg3))
Due to the CUBEVALUE function and its arguments, it's becoming a REALLY long function and thus not easy to work with. Since there are only 3 arguments, which are written in different cells, I'd like to create something like
=MyFunction(A1,A2,A3)
and use A1, A2 and A3 as "arg1, arg2, arg3" in the function mentioned first. This way its possible to "pull" the function so it would calculate using the input in B1:B3 and C1:C3 etc. as well.
The function works fine and can be pulled through and such, but my question is how to rename this loooong function into something more user-friendly, as it requires only 3 cells as an input and the rest of the text in the function just makes it hard to use for end-users.
Using UDF is not an option because CUBEVALUE can't be called through VBA... and any attempt to stich strings together and using the final result with INDIRECT also seems to fail..
In a similar question on this site, someone refers to using "asynchronous UDF's", but no further information was given (and what I could find seemed irrelevant).
You shouldn't really have several long cube functions. Allocate some space in hidden rows/columns or in header rows/columns to add your cubemember functions. Then throughout most of your report, you should just have cubevalue functions that reference other cells with error handling around them. Proper use of absolute and relative references are your friend.
Peter Meyers has some great tips for this here, slides 20 - 24. I have an example Excel file with cube functions on my blog here.

Resources