Output array formula, ignoring cells in output area that already contain data - google-sheets

I'm trying to work on a Google Sheet and make it as user friendly as possible, so when I'm not around others can't screw it up. I have previously copied the formula down the column, but I'm trying to do it in an array formula, that is in the header of the column so people don't need to copy the formula to other cells.
The formula is simply a VLOOKUP, comparing a cell to the left with the code, and retrieving the title and other information later. (I've had this working happily via copying the formula down the column.)
VLOOKUP($F$2:F, 'Lessons NEW'!$E$2:$F,2,false)
My problem comes when trying to do this using an array formula in the header. This also works properly, but doesn't allow me to do somethings needed.
={"Title";
ARRAYFORMULA(
IF($F2:$F<>"",
VLOOKUP($F$2:F, 'Lessons NEW'!$E$2:$F,2,false),
)
)}
Again this works happily.
HOWEVER. my issue is that on some rows I need to manually enter some info.
What I want to happen, is rows in the column that have data, in the array formula's destination to get skipped over and simply ignored during the array formula's output and end up containing what's been manually entered in them. (Some rows in the column will just be random manual entry stuff)
I've tried doing this via checking if there is a code in the cell to the left, and try to make it skip if so, or check if the destination cell has contents already and skip if so. but it seems that if I have anything in the output area of the array formula, it breaks it completely.
Does anyone have any suggestions of how I can accomplish this? Thanks!

Unfortunately there's no direct solution within ARRAYFORMULA. If you can, and ideally there are no in between rows added, you can add a column to the left to be hidden, and contain the formula adapted:
={"",VLOOKUP($F2, 'Lessons NEW'!$E$2:$F,2,false)}
Without ARRAYFORMULA it will be able to expand at any row that doesn't have a handwritten value. You may also protect that whole hidden column (that will mean also that no in between rows will be added by noon authorised people, may be also useful for you)
UPDATE
Sample script for copying the formula. Adapt the ranges and name of the sheet:
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
SpreadsheetApp.getUi().createMenu("Custom")
.addItem("Copy Formula","copyformula")
.addToUi()
copyformula()
}
function copyformula(){
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sh = ss.getSheetByName("Sheet3")
var range = sh.getRange("D1")
range.copyTo(sh.getRange("D2:D"))
}

what you describe contradicts the intended usage of arrayformula. arrayformula is designed to roll out and for that, it needs an empty space. any manual input in that empty space will render the arrayformula out of the game. there are only two solutions for your issue:
not use arrayformula but VLOOKUP formula in every cell
use a script that will inject the formula only if there is no manual input
and one more hybrid solution - have your arrayformula in one column and your manual input in the next column, and then have 3rd column that will gather the data like if manual column is empty output vlookup otherwise output manual input

Related

Combining Arrayformula with textjoin to eliminate delimiters

I am trying to use an arrayformula to join a string of cells in the same row as per picuture. This formula needs to automatically copy down to any new rows that are added. The formula I have so far seems to be the only one that returns at least something. I have googled and tried all sorts of join, textjoin to eliminate delimiters when a cell is empty but I keep getting all sorts of errors.
Also, note in column L I only want part of the cell, the city name, not state and zip code, which is why I added the Left function.
I would also like a header in row D1 "Summary".
Also, another problem I am having with the Arrayformula, is that any new rows that are submitted by my script, get pasted way down the bottom of the sheet, skipping blank rows. I think it sees the array formula as part of the last row even though those rows are blank. The only workaround I have so far is to delete all empty rows after my table, so I have the little [add 1000 more rows at bottom] after my last data row. But this then effects the filter range. The filter range does not seem to update automatically when new rows added. Maybe I need to address each of these issues as a new question in Stakoverflow?
Jobs Database
Delete everything from 'Jobs Database'!D:D (you can always undo it if you aren't happy with the formulas proposed). Then place one of the following two formulas in D1, though I recommend the second of the two.
The following formula will produce results in keeping with what you showed in your post and what is currently in the sheet (though my formula creates a cleaner result, because it addresses more potential issues than your original formula):
=ArrayFormula(FILTER(IF(ROW(A:A)=1,"Summary",B:B&IF(C:C="",," - "&REGEXREPLACE(TO_TEXT(C:C),"^-",""))&" - "&IF(H:H="",,H:H&" x ")&G:G&IF(I:I="",," - "&I:I)&" - "&IF(J:J="",,REGEXREPLACE(J:J,",$","")&" - ")&IFERROR(REGEXEXTRACT(L:L,"(.+)[\s\w]{8}")&" - ")&"ID: "&A:A),A:A<>""))
This formula is written to accommodate the fact that you've got column-level sorting in place. The FILTER clause surrounding everything inside also assures that the results only run as far down as you have data in Column A. This should eliminate the issue you were having with new rows being added after blanks.
I recommend the layout produced by this second formula, however:
=ArrayFormula(FILTER(IF(ROW(A:A)=1,"Summary",B:B&IF(C:C="",,CHAR(10)&TO_TEXT(C:C))&CHAR(10)&IF(H:H="",,H:H&" x ")&G:G&IF(I:I="",,CHAR(10)&I:I)&CHAR(10)&IF(J:J="",,REGEXREPLACE(J:J,",$","")&CHAR(10))&IFERROR(REGEXEXTRACT(L:L,"(.+)[\s\w]{8}")&CHAR(10))&"ID: "&A:A),A:A<>""))
In my opinion, the use of line breaks between information is cleaner and therefore easier to read.
Both formula options would require selecting all rows, right clicking, choosing "Resize rows" and selecting "Fit to data" for full viewing.
I have added two sheet ("Erik Help 1" and "Erik Help 2") with each of the above formulas in place, respectively).

Is there a way to use ARRAYFORMULA to find the most-recent even input of a column?

SOLVED EDIT
Thank you for the help. Solution here.
ORIGINAL POST
I have made a google sheet to describe the issue I am facing linked here (https://docs.google.com/spreadsheets/d/1yK6ZAX8BFnEqiuQO9HIxuY0l62ewDDccj-8EN1r2i2w/edit?usp=sharing).
I will also describe in words, below, the problem I am facing, along with the solutions I have tried.
The data of column A are random single-digit (0-9). I would like column B to show the most recent even number from column A, but only up to a specific row. That specific row is the row corresponding to the row of the cell in column B. In other words, in cell B7, I want to find the most recently entered even number of column A, specifically only on the range A2:A7 (A1 contains a column header).
This is actually a pretty simple formula, and I can get the desired outputs by simply checking if the value in a cell in column A is even and then returning the value of that cell if it is, or the output of the cell above if it isn't. So the formula would look something like: ​=IF(ISEVEN(A7),A7,B6)​
However, my problem is that the length of the data in column A will be growing as more data are entered, and my current solution of using the fill handle to copy the formula to new cells is inelegant and time-consuming. So my desired solution is to use an array formula entered into the first cell of column B (B2), capable of returning the same value as the other formula. The formula I tried to enter to perform this was the following: ​=ARRAYFORMULA(IF(ISEVEN(A2:A),A2:A,INDIRECT(ADDRESS(ROW(A2:A)-1,2))))​
However, as some of my previous work with arrays has taught me, not all formulas iterate as expected down the array. The formula seems to be able to return the correct output on lines which are already even, but it is unable to return the expected most-recently entered even number for all the other lines. It appears that the formula is not able to appropriately interpret the ​value_if_false​ argument of the ​IF​ formula.
I'm a little new to scripting, so I'm still trying to learn, but I also tried to dabble around with custom functions to no avail. I'm still wet behind the ears when it comes to coding, which is why I've been so lenient on the built-in formulas of Google Sheets, but I fear I may have reached the limit of what Sheets formulas can do.
I am open to trying new approaches, but my only real constraint is that I would really like for this to be a one-touch (or even better no-touch) solution, hope that's not too far beyond the scope of this issue. Any assistance would be much appreciated.
EDIT
After rubber-ducking the problem here, I went back and tried to use the OFFSET formula, hoping I could get it to play nicely with the array formula. Alas, I was unable, but I thought I should at least post my progress here for reference.
Attempt with offset
Still working at it!
Doing a vlookup on the row number seems to work for me
=ArrayFormula(if(A2:A="","",vlookup(row(A2:A),{if(iseven(A2:A),row(A2:A)),A2:A},2)))
Note: if there are no even numbers in range for some rows, it will produce #N/A for those rows.

Return column header for last date in Google Sheets as an array formula

I've got a number of columns used to track some stages of Google Sheets entries with dates. I'd like a column that returns the latest stage for each entry, preferably using an array formula since this is a list that will be constantly added-to and copying the formulae down to new rows is a pain (this is something that will end up in with an end-user so needs to be straightforward).
This is a sample of my desired input/output.
I tried using array formulae combined with this lookup trick, but I think changing the inputs of that formula to multi-row ranges means the result ignores what row the entry is on. For instance wrapping the lookup in ARRAYFORMULA and applying it to the sample returns "Stage4" on every line. Alternatively I think there might be a way to do this using QUERY/FILTER but I'm getting nowhere. Thanks everyone.
EDIT: For clarification, the 'Stages' are not going to be numbered, but will be text fields (potentially dynamic ones, hence my reticence to use an HLOOKUP).
Assuming "Stage1" is in cell A2 and that you can not jump stages. You can use this formula on F3 to auto-populate your output:
=ArrayFormula(transpose(
split(
concatenate(
if((A3:D<>"")*(B3:E=""),A2:D2,"")&
if(column(A2:D2)=column(D2),char(9),"")
)
,char(9),true,false
)
))
You can see that we're determining the stage by checking the not empty that is followed immediately by an empty one. Hence the no jumping stages rule :-)
Also, this requires an extra empty column after the last stage column (in your example, column E must be empty).
ps: added bonus, this formula also works for "no stage" rows ;-)

ARRAYFORMULA with IMPORTRANGE

In column B are listed IDs of Google Sheets. In column C are listed cells, from which I want to import data.
Screenshot of the table
In column D is shown the result of using IMPORTRANGE() by simply dragging it. e.g. for D1 it looks like:
=IMPORTRANGE(B1;C1)
for D2:
=IMPORTRANGE(B2;C2)
and so on.
In column E I want to display the same result but using ARRAYFORMULA that looks like:
=ARRAYFORMULA(IMPORTRANGE(B2:B4,C2:C4))
but the function displays only the data from the first spreadsheet.
People complain about this permissions issue a lot, but it's not hard to solve. What I do is have a sheet which I name "Splash sheet" into which I paste the URLs of the documents I wish to link. To its right is a column headed "permit to connect" which contains IMPORTRANGE formulas importing a single cell from each sheet -- usually a cell containing a confirmation code, number or document name -- on a sheet also named "Splash Sheet." For example,
=IF(B3="enter URL",,CONCATENATE(IMPORTRANGE(B3,"Splash sheet!A1")," ",IMPORTRANGE(B3,"Splash sheet!B1")))
So, when you first connect a spreadsheet via its URL, you get those messages telling you you need to connect, you click the Permit Access, the confirmation code/number/document name appears in the second column, and voilá, your sheets are connected forevermore! Now all your other IMPORTRANGEs referencing that URL will work, and you can use IMPORTRANGE formulas that reference the URL-containing cells on the "splash sheet."
As for the OP's original question, I came here seeking an answer to the same problem, and after more research have realized that we are attempting the impossible here. No way to do this an ARRAYFORMULA. No way around writing formulas that reference every single cell a document's URL may go into.
Problem is you can't make arrays of arrays in spreadsheets; that would involve multiple dimensions, and the medium is inherently two-dimensional. This is what people use databases for.
ARRAYFORMULA doesn't work when importing data (I think it relates to permissions). You could use something like this, =IFERROR(IMPORTRANGE(B5:B7;C5:C7)) and pre-fill the column first, but still there would be the permissions issue. Each new imported sheet needs it's permissions granted by a user.
TLDR: If I understand your intention correctly when you say you would like to see
=ARRAYFORMULA(IMPORTRANGE(B2:B4,C2:C4)), I believe you can make that
happen using the following.
=ARRAYFORMULA(IMPORTRANGE(
INDIRECT(ADDRESS(ROW(B2:B4), COLUMN(B2:B4)),
INDIRECT(ADDRESS(ROW(C2:C4), COLUMN(C2:C4))
)
Breakdown
Use IMPORTRANGE with INDIRECT to create ranges inside ARRAYFORMULA
Call INDIRECT with the ADDRESS function
Call ADDRESS with the ROW and COLUMN functions since they take ranges via ARRAYFORMULA
using IMPORTRANGE with INDIRECT
IMPORTRANGE's two parameters are the spreadsheet url stored in B2:B4 for this example and the range (e.g. sheet!A1:B2) stored in C2:C4.
Since IMPORTRANGE doesn't take a range reference directly as you mentioned, you'll need to build it for each row with ARRAYFORMULA using the INDIRECT function.
INDIRECT can be used to compose a cell reference using A1 notation, for instance
=IMPORTRANGE(INDIRECT("B" & 2), INDIRECT("C" & 2))
will produce the same result as
=IMPORTRANGE(B2, C2)
Since this produces the same result, we now just have to find a way to make INDIRECT work with ARRAYFORMULA
Use ADDRESS to build the parameters for INDIRECT
Next you want to use ADDRESS to build the A1 reference for INDIRECT. For the current purposes, ADDRESS takes a numerical value for row and column as parameters
=INDIRECT(ADDRESS(2,2))
will produce the same result as
=INDIRECT("B" & 2)
Since these two are interchangeable, now we just need to find a way to get the numerical row and column values out of ARRAYFORMULA.
Call ADDRESS using the ROW and COLUMN functions
From there, you can get the row and column indexes from standard A1 notation using the ROW and COLUMN functions. While this may seem like we're pointlessly going in circles, the difference now is that ROW and COLUMN perform as expected with the ranges provided by ARRAYFORMULA. So given that ADDRESS will return $B$2 using using either method below
=ADDRESS(2,2)
or
=ADDRESS(ROW(B2),COLUMN(B2))
we now know that
=ARRAYFORMULA(ADDRESS(ROW(B2:B4),COLUMN(B2:B4)))
will produce the following array of addresses
{ $B$2; $B$3; $B$4 }
Final Assembly
So when we put this all together, we get
=ARRAYFORMULA(IMPORTRANGE(
INDIRECT(ADDRESS(ROW(B2:B4), COLUMN(B2:B4)),
INDIRECT(ADDRESS(ROW(C2:C4), COLUMN(C2:C4))
)
where INDIRECT(ADDRESS(ROW(B2:B4), COLUMN(B2:B4)) is more or less interchangeable with what you might expect from B2:B4 inside ARRAYFORMULA and represents the url parameter
and INDIRECT(ADDRESS(ROW(C2:C4), COLUMN(C2:C4)) is roughly interchangeable with what you might expect from C2:C4 inside ARRAYFORMULA and represents the range parameter.
Suggestions on organization
I recommend using the indentation (Alt +Enter to create a new line ) above along with your indentation of choice to keep it easier to read. In the end it's just a bit more syntactic sugar and if spaces are used well it shouldn't be much harder to understand and make changes to 6 months later.
RE: Permissions - as mentioned by Atiq Zabinski, just placing a simple
IMPORTRANGE("http:/xxxx", "A1") somewhere on the sheet will provide a
means to know if the sheet is connected or not and the error message
should give you a context menu for connecting the sheet. You'll might
want to stay away from error handling in these scenarios as it will
slow down the process of connecting the sheets.

Exporting values of unchecked items in Google Forms checklist

Using Google Forms checkboxes to collect survey responses into a Sheets doc, I find that I can only get the values of checked lines to fill the spreadsheet.
I'm trying to get the unchecked CheckBox values to fill a different cell in the spreadsheet as well, has anyone found out a way to do this?
I'd appreciate any help!
I have a way with spreadsheet formulas.
The drawbacks are the necessities of
manual input of all possible options
copying and pasting a formula for each response (can be done in advance, but if not enough formulas are pasted for the number of responses, the sheet will break)
An ideal solution would automatically detect all possible responses, and could theoretically accept an infinite number of responses without breaking and needing maintenance.
With that said, he's an example of what I suggest:
Google Sheet
Google Form
All possible options on the form must be typed into cells A2:A
cells B2:B show comma+spaces if the corresponding cell in A2:A is not blank, with =arrayformula(if(isblank(A2:A),"",", ")). These commas and spaces are needed later in a concatenate().
Raw data from the Form Responses spreadsheet was brought into D2:E with =arrayformula('Form Responses 1'!A2:B)
G2:G holds the important formulas. They must be copied and pasted to each individual cell for each row.
First, all possible responses in $A$2:$B are filtered based on if the cell in column A can be found in the text in column E.
=filter($A$2:$B,arrayformula(iserror(search($A$2:$A,E2))))
Next, some error catching. I added an iferror() to catch a "N/A, no results found in filter evaluation", and an if(isblank(),"",) to catch if no timestamp is in column D meaning no response was recorded in this row.
=if(isblank(D2),"",iferror(filter($A$2:$B,arrayformula(iserror(search($A$2:$A,E2)))),""))
Finally, do a concatenate() to pull the values and the comma+spaces all into one string inside one cell.
=concatenate(if(isblank(D2),"",iferror(filter($A$2:$B,arrayformula(iserror(search($A$2:$A,E2)))),"")))
Oh, and if you don't like the comma+space being the last characters in the string, in J2:J I did a =arrayformula(iferror(left(H2:H,len(H2:H)-2),"")) which cuts off the last 2 characters of the string in column H.
If I come up with a better solution, I'll let you know. It's an interesting puzzle to solve.

Resources