Related
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.
I have a sheet containing the following data:
URL A, URL B, similar score in percent.
If URL A is 98% similar to URL B, it means that URL B is 98% similar to URL A, and listed as well.
I want to find and eliminate these duplicates/reversed entries. For now, I have tried having two extra columns concatenating URL A+URL B in one, and URL B+URL A in one. This way I have unique identifiers.
After this I'm kinda stuck, because I'm dealing with a lot of variables, as data is in two different rows, and two different columns. I might be looking into a script, taking the A+B value, iterating through the B+A value until it finds a match, and somehow marks this (or simply just deletes it), since my knowledge of formulas for highlighting these duplicates are falling short.
This sheet shows the concept - the first 100 rows (it's about 11K in total): https://docs.google.com/spreadsheets/d/1YKsguAn1lYjV4FlP_6_TlKGvFcpFAEzn7bpAyOEmozQ/edit?usp=sharing
Any suggestions for what I should look into?
Try the filter(match()) pattern to find duplicate values, like this:
=unique(
flatten(
filter(
A2:B,
match(A2:A & B2:B, B2:B & A2:A, 0),
C2:C >= 90
)
)
)
I ended up with a solution where I sorted by URL A and implemented this formula:
=IF(A2<B2,A2&B2,B2&A2)
This way I had the concatenation the same way for the real one and the opposite. I didn't know you could use "<" on strings.
After this, I could delete duplicated values in the column with the formula above.
I have the following (simplified from original) formula which pulls in data from another sheet (multiple sheets in the original formula) and calculates the sum of a column based on the month() value of another column:
=QUERY({
IFERROR(IMPORTRANGE('C1'!$B$5, "PLACEMENTS!A4:L1000"), {"",TO_DATE(0),"","","","","","","","","",""})},
"SELECT SUM(Col8) WHERE Col2 IS NOT NULL AND month(Col2)+1="& MONTH($A3) &" AND Col1="""&B$2&""""& IF(NOT($C$1="ALL"), "AND Col6="""&$C$1&"""" ,)& " label SUM(Col8) ''",0)
I keep getting the #VALUE! Error:
Unable to parse query string for Function QUERY parameter 2: Can't
perform the function month on a column that is not a Date or a
DateTime column
If I remove the IFERROR wrapper to the IMPORTRANGE statement then the error goes away and as you can see, in the case of an error on the IMPORTRANGE statement, an empty array with Col2 set as a date with TO_DATE(0) is inserted.
This formula without the IFERROR works fine, and pulls in rows and provides a SUM value:
=QUERY({IMPORTRANGE('C1'!$B$5, "PLACEMENTS!A4:L1000")},"SELECT SUM(Col8) WHERE Col2 IS NOT NULL AND month(Col2)+1="& MONTH($A3) &" AND Col1="""&B$2&""""& IF(NOT($C$1="ALL"), "AND Col6="""&$C$1&"""" ,)& " label SUM(Col8) ''",0)
Here's a link to a sample sheet that the formula imports from with the same structure of data: Sample Sheet Data
If the Query works without the IFERROR wrapper, why does it produce the error about Col2 with the IFERROR wrapper in place which shouldn't be triggered in this case? This exact same formula works fine on another user's spreadsheet. Is this a Google Sheets/Query bug or is there something I'm doing wrong here? Thanks
IFERROR(exp1,exp2) is equivalent to IF(NOT(ISERROR(exp1)),exp1,exp2)
However, while the first option is not always compatible with queries, the second is more so.
In your case changing
IFERROR(IMPORTRANGE('C1'!$B$5, "PLACEMENTS!A4:L1000"), {"",TO_DATE(0),"","","","","","","","","",""}
to
IF(NOT(ISERROR(importrange('C1'!$B$5, "PLACEMENTS!A4:L1000"))),importrange('C1'!$B$5, "PLACEMENTS!A4:L1000"), {"",TO_DATE(0),"","","","","","","","","",""})
will solve the problem.
You have not shared the Sheet Snapshot you're working on, and it is hard to know what is happening in your formula.
Answer Updated
IFERROR function is giving {"",TO_DATE(0),"","","","","","","","","",""} incase first parameter is false and that is the array which is generating the error.
In the Query function in Select clause, you're doing Sum(Col8) which need a number for summation, but in array, at location 8 you're giving an empty string. You just need to add zero at index 8 {"",TO_DATE(0),"","","","","",0,"","","",""}
How do you adjust comma separated values in such a way that the value separated with commas is separated and that a new row is created for this value and that the other values are the same as in the row from which the value comes? That would look like this:
From this..
..to this.
I'm actually looking for an answer that doesn't use google script when possible and without using gigantic long and complex formulas. The use of a pivot table within Google sheets may be used, but is also not my preference. But if it's not possible to use only formulas then I'm open to other answers as well.
I've had this question for over a year and I can't find serious answers online after a few hours of searching. There will be answers using a google script, but that doesn't really fall within the scope of my question. I am willing to adjust or rephrase my question if the current question remains unanswered.
I myself have no idea how to answer the question and the attempts I have made are not to be taken seriously.
Lambda Update
It's 2022. We now have LAMBDA and a bunch of array functions. Thus we can combine everything into a single formula, as originally desired. The idea is still the same as before, just much cleaner. (Also FLATTEN is no longer undocumented.)
=ArrayFormula(
SPLIT(
TRANSPOSE(
SPLIT(
JOIN(
"",
BYROW(
A1:E4,
LAMBDA(row,
JOIN(
",",
REDUCE(
";",
row,
LAMBDA(cell1,cell2,
FLATTEN(FLATTEN(cell1)&","&SPLIT(cell2,","))
)
)
)
)
)
),
";,",
)
),
","
)
)
How it works
REDUCE combines all elements in an array to a single result using a function (Named Function or LAMBDA). In this case, we use the same permutation trick (combine column vector with row vector) as in the old solution to serialize every row. This gives us a column of rows, each starting with ;,
The rows are JOINed together, serializing the array.
BYROW applies a function to each row in a range, returning a single value for each row. Here, we use the process above in a LAMBDA function which gives a single serialized string for each row in the range.
We then JOIN all these together. Each row is delimited by ;,.
Split on the ;,, giving a row of serialized rows
Transpose the row to get a column
Split again on , deserializing each row.
For a much more readable solution, of course, you can name the lambdas, resulting in a formula that's cleaner still.
Draggable Formula solution (obsoleted by LAMBDA)
Let's see if I can get this ball rolling.
At a Glance
This solution is unfortunately unstable, as it relies on the Flatten undocumented function (turn any range into a column array), and requires two formulas to work. While I'm sure that you can do the same thing without Flatten(), this at least saves us some typing, as we rely on it heavily. Without flatten, we can achieve the same with TRANSPOSE(SPLIT(TEXTJOIN(...)), which is not nearly as elegant.
The core formula, while it does have a linear growth factor and can get messy with more columns, does have an easy pattern to follow for the setup. It can also be dragged, which is the next best thing to a single ArrayFormula.
Stage 1: Serialize Rows
As you might have expected, we're going to use some string serialization tricks to get what we want. Here's the core formula:
=TEXTJOIN(",",,
ArrayFormula(
Flatten(Flatten(Flatten(Flatten(Flatten(
SPLIT(A1,",")&",")&
SPLIT(B1,",")&",")&
SPLIT(C1,",")&",")&
SPLIT(D1,",")&",")&
SPLIT(E1,",")&";")
))&","
As you can see, it accounts for any commas inside each cell in the row. To add more columns, simply add another Flatten( and add your column to the list. Just make sure that the last one uses a ; and not a ,.
We take advantage of the fact that, in general, when ArrayFormula is applied to a column vector and a row vector, we can do an operation on every permutation of the two mixed together.
Examples:
=ArrayFormula({0;1}&{2,3}) is equivalent to ={"02","12";"03","13"}
=ArrayFormula(SEQUENCE(10)*SEQUENCE(1,10)) gives us a 10x10 multiplication table.
In our case, we use this to generate every possible permutation of rows based on the commas in each cell, serializes the row into a CSV string, ending each in a semicolon, then joins all the rows into one long string. The extra "," is so we can concatenate multiple tables together in the next stage.
When you're set up with the proper number of columns, drag this down to the height of the table. (Note: If some of your values can be blank, you also have to do some error checking around each SPLIT.)
Stage 2: Deserialize
This formula is considerably simpler. (Assuming serialization data is in column F.)
=ArrayFormula(
SPLIT(
TRANSPOSE(
SPLIT(
JOIN(,F:F),
";,",
)
),
","
)
)
First, glue all the strings together using JOIN. Since we know that each row ends with a ";,", we split on that to get our rows. After that, we can split each row up into cells by splitting on ",", resulting in our table.
Conclusion
It's not a single ArrayFormula, sure, but neither of these formulas is really all that complex, which is nice. ArrayFormulas can get messy and confusing quickly.
We've managed to avoid scripting too, which is a plus.
You can also hide the serialization column if you find it unsightly!
I have a tournament spreadsheet that has a list of names in column "D" (between 20 and 150 entries) and the table number that each person is assigned to (column "E").
In column "I", I have a ridiculous formula that creates a list of the number of seats available at each table (this information changes from one event to another).
Column "G" is my problem. I want to run through the contents of column "E" and anytime there is a value in column "E" that matches the contents of column "I", to give the the contents of column "D".
Here is my formula as it currently stands in cell G3:
{=IFERROR(INDEX(D$3:D$150,SMALL(IF(E$3:E$150=I3,ROW(E$3:E$150)-ROW(E$3)+1),ROWS(E$3:E3))),"")}
The formula works perfectly for all values in column "E" as long as the value in column "E" is 1. This formula works beautifully in Google Sheets but Excel seems to process the ranges differently. I've tried with both a standard formula and an array formula without success.
Any advice would be greatly appreciated.
One additional note, I can use VBA if needed but I would prefer to stick with functions.
for this example I used the following formula:
=INDEX($C$14:$C$25,AGGREGATE(15,6,(ROW($D$14:$D$25)-ROW($D$14)+1)/($D$14:$D$25=H16),COUNTIF($H$16:H16,H16)),1)
You will need to adjust the ranges to suit your needs.
From my testing, the problem appears to be with the ROWS(E$3:E3) as the second argument for the SMALL function. Basically, when you get down to your first 2 in row 12, you are asking the small function for the 10th smallest value in D that has 2 in column E. Since it looks like you are only expecting eight players, this will never find anything.
Try changing ROWS(E$3:E3) to COUNTIF(I$3:I3, I3).
That will count how many 1's or 2's you've already passed in column I, so when you get to the 2's, it should reset and ask for the first smallest.
#Forward Ed. Thanks for your input. That helped get me on the path that I needed.
Here is the formula that ended up working for me:
{=IFERROR(INDEX(D$3:D$150,SMALL(IF(E$3:E$150=I3,ROW(E$3:E$150)-MIN(ROW(E$3:E$150))+1),J3)),"")}