I am working with a large (but simple) formula In Google Sheets that re-uses the same blocks of formulas repeatedly. To get a bunch of data from a bunch of different tabs I have to use 708 characters in that block of formulas. But then I need to repeatedly reference that data over and over within just the 1 cell which multiplies the length of the formula to the point where I can't even tell what is going on any more.
For example I have a cell with the final code (with 2216 characters) of:
=iferror(IF(ISNUMBER(SEARCH("a",concatenate(TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Brown!$C$3:$C$68&Brown!$D$3:$D$68,Brown!H$3:H$68,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Godoy!$C$3:$C$76&Godoy!$D$3:$D$76,Godoy!H$3:H$76,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Sindel!$C$7:$C$60&Sindel!$D$7:$D$60,Sindel!H$7:H$60,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Taylor!$C$3:$C$82&Taylor!$D$3:$D$82,Taylor!H$3:H$82,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Wanner!$C$3:$C$55&Wanner!$D$3:$D$55,Wanner!H$3:H$55,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Gehrman!$C$3:$C$16&Gehrman!$D$3:$D$16,Gehrman!H$3:H$16,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Francois!$C$3:$C$17&Francois!$D$3:$D$17,Francois!H$3:H$17,"")))))),"A",average(ArrayFormula(mid(concatenate(TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Brown!$C$3:$C$68&Brown!$D$3:$D$68,Brown!H$3:H$68,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Godoy!$C$3:$C$76&Godoy!$D$3:$D$76,Godoy!H$3:H$76,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Sindel!$C$7:$C$60&Sindel!$D$7:$D$60,Sindel!H$7:H$60,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Taylor!$C$3:$C$82&Taylor!$D$3:$D$82,Taylor!H$3:H$82,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Wanner!$C$3:$C$55&Wanner!$D$3:$D$55,Wanner!H$3:H$55,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Gehrman!$C$3:$C$16&Gehrman!$D$3:$D$16,Gehrman!H$3:H$16,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Francois!$C$3:$C$17&Francois!$D$3:$D$17,Francois!H$3:H$17,"")))),sequence(len(concatenate(TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Brown!$C$3:$C$68&Brown!$D$3:$D$68,Brown!H$3:H$68,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Godoy!$C$3:$C$76&Godoy!$D$3:$D$76,Godoy!H$3:H$76,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Sindel!$C$7:$C$60&Sindel!$D$7:$D$60,Sindel!H$7:H$60,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Taylor!$C$3:$C$82&Taylor!$D$3:$D$82,Taylor!H$3:H$82,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Wanner!$C$3:$C$55&Wanner!$D$3:$D$55,Wanner!H$3:H$55,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Gehrman!$C$3:$C$16&Gehrman!$D$3:$D$16,Gehrman!H$3:H$16,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Francois!$C$3:$C$17&Francois!$D$3:$D$17,Francois!H$3:H$17,"")))))),1)*1))),"")
This looks crazy long, but it is only because I am using this one formula (with 708 characters):
concatenate(TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Brown!$C$3:$C$68&Brown!$D$3:$D$68,Brown!H$3:H$68,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Godoy!$C$3:$C$76&Godoy!$D$3:$D$76,Godoy!H$3:H$76,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Sindel!$C$7:$C$60&Sindel!$D$7:$D$60,Sindel!H$7:H$60,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Taylor!$C$3:$C$82&Taylor!$D$3:$D$82,Taylor!H$3:H$82,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Wanner!$C$3:$C$55&Wanner!$D$3:$D$55,Wanner!H$3:H$55,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Gehrman!$C$3:$C$16&Gehrman!$D$3:$D$16,Gehrman!H$3:H$16,""))),TEXTJOIN("",TRUE,arrayformula(if($B3&$C3=Francois!$C$3:$C$17&Francois!$D$3:$D$17,Francois!H$3:H$17,""))))
3 times within the cell.
Is it possible to have one cell just contain the block of functions that I want to use (as a string) and then somehow convert the string to code to reuse it without making a monster function?
For example, could I assign A1 to hold the long code that I want to have multiple times and then have a formula like:
=IFERROR(IF(ISNUMBER(SEARCH("a",textToFormula(A1))),"A",AVERAGE(ArrayFormula(mid(textToFormula(A1),sequence(len(textToFormula(A1))),1)*1)).
I should also mention that there is no room in my sheet to just put the string of data I am looking for in a separate cell, because I have to apply this formula roughly 50 rows and 180 columns.
Ouch! That is a long formula! Sadly, there's no eval() like in JavaScript, but we can at least make you a simpler formula.
How about this one? It's still a bit long, but far less complex. It only Queries each sheet once. This one works in cell F3, but can be dragged.
=IF(
JOIN("",{Teacher1!G$7:G;Teacher2!G$7:G;Teacher3!G$7:G;Teacher4!G$7:G;Teacher5!G$7:G})<>"",
IFERROR(Average(ArrayFormula(--{
QUERY(ArrayFormula(TO_TEXT(Teacher1!$A$7:$GX)),"select Col"&COLUMN()+1&" where Col3='"&$B3&"' and Col4='"&$C3&"'");
QUERY(ArrayFormula(TO_TEXT(Teacher2!$A$7:$GX)),"select Col"&COLUMN()+1&" where Col3='"&$B3&"' and Col4='"&$C3&"'");
QUERY(ArrayFormula(TO_TEXT(Teacher3!$A$7:$GX)),"select Col"&COLUMN()+1&" where Col3='"&$B3&"' and Col4='"&$C3&"'");
QUERY(ArrayFormula(TO_TEXT(Teacher4!$A$7:$GX)),"select Col"&COLUMN()+1&" where Col3='"&$B3&"' and Col4='"&$C3&"'");
QUERY(ArrayFormula(TO_TEXT(Teacher5!$A$7:$GX)),"select Col"&COLUMN()+1&" where Col3='"&$B3&"' and Col4='"&$C3&"'")}
)),"A"),
""
)
The Query Statement:
Each cell queries each sheet as a table where the name is matched in the sheet as a row.
The COLUMN()+1 is to get the corresponding columns to line up. I.e. If we're in column F (6), we want to look in column G (7).
The TO_TEXT allows us to look for non-numbers ("A").
After that, convert each query result to a number with --, then take the Average. If any of the numbers cannot be converted to a number, Average gives us an error, and we assume the value was "A".
In the case that all cells in a column for a date are blank (the blank JOIN), bypass the queries altogether and output a blank cell.
In this sheet, I've the below input data:
As seen, the courses are separated by /
I want to display the same in the format below, where each line shows one course only, with the data of the student repeated:
I know using =split(C3," / ",true,true) can split the courses into 2 columns at the same row, but I need them in the same column, so I tried =TRANSPOSE(split(C3," / ",true,true)) that is working fine for the first line only, but it fail with using ARRAYFORMULA.
Any thought? I'm opened for any potential solution, formula or script or any other.
UPDATE
I tried this trick, creating a new column showing number of courses for each student as =ArrayFormula(LEN(REGEXREPLACE(C11:C13, "[^/]", ""))+1)
Then using Rep to repeat each row based on the number of courses =arrayformula({transpose(split(concatenate(rept(B11:B13 & ",",D11:D13)),",",false,true)),transpose(split(concatenate(REPT(C11:C13 & ",",D11:D13)),",",false,true))}) then ended up with:
But here, I've the courses still joint together, how can i split them!
I've added two sheets to your sample spreadsheet. "Sheet2" is a cleanup of your testing sheet, "Sheet1." The other sheet ("Erik Help") references Sheet2, not Sheet1, and contains the following formula in cell A1:
=ArrayFormula({"Student ID","Student Name","Course";SUBSTITUTE(SPLIT(QUERY(FLATTEN(SPLIT(FILTER(SUBSTITUTE("/ "&Sheet2!C3:C,"/","/ "&Sheet2!A3:A&"zzz~"&Sheet2!B3:B&"~"),Sheet2!A3:A<>""),"/")),"Select * WHERE Col1 Is Not Null"),"~"),"zzz","")})
This one array formula produces all headers and results.
A virtual array is formed between the curly brackets { }. Headers are introduced first followed by a semicolon, which means "bump down one row to continue." The header titles can be changed as you like.
How It Works:
An addition "/ " is concatenated to the front of every non-blank entry in Sheet2!C2:C. Then SUBSTITUTE replaces every one of these forward slashes with Col A data, "zzz~", Col B data and "~". The tildes (~) will be used later by the outer SPLIT. The "zzz" is added to make sure that ID numbers are converted to text so that they hold formatting throughout the processing and don't turn into real numbers; later, the outer SUBSTITUTE will replace those with null (i.e., get rid of the 'zzz').
Once the initial concatenations are complete, they are SPLIT at the forward slash and then FLATTENed into one column. QUERY removes any blank rows in this virtual array so far. The remaining results are again SPLIT at the tilde. Finally, that outer SUBSTITUTE removes the temporary instances of 'zzz'.
I also added a custom CF formula for the alternating color banding on alternate rows.
You can try this one:
Formula:
=ARRAYFORMULA(TRIM(QUERY(SPLIT(FLATTEN(IF(IFERROR(SPLIT(C3:C5, "/"))="",,
A3:A5&"×"&B3:B5&"×"&SPLIT(C3:C5, "/"))), "×"),
"where Col3 is not null")))
Output:
Reference:
How to transpose & split multiple columns and repeat specific cells in a column
Is it possible to have 2 custom functions in one cell?
I currently have one function and another function connected through a & symbol. But what ends up happening is that both of them run at the same time, thus producing two results in one cell. I want it to be so that when one is true the other one won't be seen and only the result of one will be showing.
Any help?
Just use IF
=IF(fn1(B1), fn1(B1), "") & IF(fn2(B1), fn2(B1), "")
if fn1() returns false the first part will be an empty string "" otherwise it will be the result. Same for the second part.
IF documentation
I was originally building a formula to count how many cells were being filled in on a sheet different from the one that I was working on. I chose a set of blank cells on Sheet1 with the following formula:
=COUNTA(ADDRESS(3,5,4,1,"Sheet1") & ":" & ADDRESS(4,12,4,1))
The result was 1 for a set of blank cells. The equivalent formula - =COUNTA(Sheet1!E3:L4) - returns the correct answer of 0.
Why don't I get the same result?
Background: in the actual formula that I was using, I'd replaced the 3 & 4 in the ADDRESS function with a ROW() calculation to choose 2 rows at a time on Sheet1 1 row on Sheet2. I was testing the ADDRESS function for use by hardcoding the row addresses.
ADDRESS creates a string, not an actual Cell Reference. So, =ADDRESS(3,5,4,1,"Sheet1") is ="Sheet1!E3", not =Sheet1!E3.
To convert a String to a Cell Reference, use the INDIRECT function: =INDIRECT("A1") is the same as =A1.
Stick it together, and...
=COUNTA(INDIRECT(ADDRESS(3,5,4,1,"Sheet1") & ":" & ADDRESS(4,12,4,1)))
(I hope you are going to change some of those 'magic numbers' to functions or cell-reference, otherwise you could just use =COUNTA(Sheet1!E3:L4) instead!)
I use the status bar in nearly all my macros . If implemented correctly , allows you to see where your code is at.
E.g
SysCmd(4, "Sorting...")
SysCmd(5)’Set status bar back to normal
Question - How do I get the status bar to display a cell value?
E.g
SysCmd(4, "Sorting...") & xl.("Sheet1").Range("A1")
Above doesn’t work, but is there a way of doing this, in Access VBA ?
When you use 4 (acSysCmdSetStatus) as the first argument to SysCmd, you can include only one additional argument ... so two arguments total, not three.
But you can combine your proposed second and third arguments into one string, and SysCmd will cooperate ...
SysCmd acSysCmdSetStatus, "Sorting... " & xl.("Sheet1").Range("A1")
Note I assumed xl in your cell reference, xl.("Sheet1").Range("A1"), is an Excel workbook object. Since it turns out xl is an application object, Sobigen's suggestion, xl.activeworkbook.sheets("Sheet1").Range("A1"), makes sense to me.
But whatever the correct cell reference is, take that and concatenate it with whatever else you want to appear in the status text ... and use that entire string as the second argument to SysCmd