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

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 ;-)

Related

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

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

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).

In array formula, find last specific value up till each row

I have a Google Sheets in question. Here is a mocked version that you can copy or comment on: https://docs.google.com/spreadsheets/d/1szcfZt_CtE0Cd_evKJwN4enZE7JcmDFcw9XhoX-_mlw/edit?usp=sharing
The sheets comes with 2 columns, Test number and Test result. I'd like to use array formula to automatically fill column C whenever new tests are entered. The specification is that the number in column C should always refer to the last "Fail" test number up till each row. You can see the expected result in column E.
I can come up with many solutions that require a formula to be applied to each new cell, which I think is iterative approach. One example is in column G. The reason is that "up till the current row" can't be met with array formula. I tried QUERY function with no success. I know App Script can do this too, but that's not auto fill approach as well.
EDIT: Added a brute-force solution, which can only work with limit number of consecutive "Pass" results.
Please see the screenshot for a solution using an array formula.

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.

Conditionally format first instance of value in column

I'm trying to style the first instance of a value in a column. I found this custom formula through googling:
=COUNTIF($A1:$A100,$A1)=1
but this styles the last instance of the value, and I'm not sure why.
try this formula:
=COUNTIF($A$1:$A1,$A1)=1
THE COUNTIF SOLUTION:
The formula =COUNTIF($A$1:$A1,$A1)=1 as suggested by Max is a common solution to this problem. It is a variation of the formula for finding duplicates : =COUNTIF($A:$A,$A1)>1.
COUNTIF DRAWBACK:
One of the drawbacks of using the COUNTIF formula is that it relies on the first parameter $A$1:$A1 in order to accurately evaluate the conidtional-formatting correctly. The formula works the same in the conditional formatting as it would if you were to physically put the formula in B1, and the copy it down the whole column. The first copy in B1 will appear as the original formula =COUNTIF($A$1:$A1,$A1)=1but the one in B2 will appear as =COUNTIF($A$1:$A2,$A2)=1.
This can be a real problem and result in false positives or maybe the conditional formatting not working at all if you are doing any sorting, cutting and pasting, dragging and dropping rows or cells, etc.
THE MATCH SOLUTION:
An improved version of this formula that eliminates the possibility of false positives and prevents the range from automatically being updated when it has been sorted, copied, cut, dragged, dropped, etc is as follows:
=MATCH($A1,INDIRECT("$A:$A"),0)=ROW()
EXPLANATION OF MATCH SOLUTION:
The only purpose in the INDIRECT formula is to prevent the range from automatically updating. If you would prefer it to update when you copy and paste you can instead do: =MATCH($A1,$A:$A,0)=ROW() The key to this formula working properly is that the MATCH formula parameter 2 looks at the entire column, that way when it finds the exact location of parameter 1 it can compare it to the row#. If there are duplicates within column A Match will only return the location of the first instance. Since parameter 2 is the entire column the answer it returns is also the row# of the first instance. So the second part of the formula above =ROW() will compare the first instance's row# to the row# of the current cell, if they are identical than the formula will entire formula will return TRUE
ADAPT MATCH SOLUTION TO FIND DUPLICATES (after first instance):
The MATCH formula can also be adapted to find all duplicates after the first entry. (basically the inverse) by changing the last part of the formula =ROW() into <ROW() So the duplicate finding formula would be: =MATCH($A1,INDIRECT("$A:$A"),0)<ROW()

Resources