Google Sheets Recursive Array Formula - google-sheets

I'm trying create a list of sequential dates from a set date to the most recent date in another column.
=ARRAYFORMULA(IF(OR(A2:A=MAX(C:C),A2:A=""),"",DATEVALUE(A2:A+1)))
I use MAX() to find the dates most recent date in column C. What I'm trying to get this formula to do is to recursively check the date in the cell above to determine if the max date has been reached. I've made sure A2 already has the set starting date.
The output is in only one cell though and I don't know why.
Thank you for the help.

What appears to be troubling your current array formula is that as defined it goes on forever and is self-referential. There may be a way to make it by setting preferences to be iterative and helping it interpret getting to repeated cells with "" as convergence.
Here is a way to sidestep those issues. You can bulletproof it more (for example, by encasing it in an IFERROR), but basically you can calculate exactly how many entries you need, and then set your range in the Array formula accordingly. In A3 you place the following:
=arrayformula(DATEVALUE(Row(indirect("A3:A"&(max(C:C)-A2+2)))+A2-2))
which will construct the exact range you want, then calculate each entry with an explicit rather than recursive formula.
EDIT: the above implementation assumes you need at least 2 dates. You can handle that case and other weird ones with the following, less readable formula, =if(max(C:C)>A2,iferror(arrayformula(DATEVALUE(Row(indirect("A3:A"&(max(C:C)-$A$2+2)))+$A$2-2))),"")

Related

Xlookup issue with Indirect function in Google Sheets

I am utilizing a xlookup with indirect references function with certain date ranges to determine the range of rows to search in. With 3k rows of data and repeating values this helped me pair down to non-repeating values so I don't have an errant value from an earlier entry. The xlookup initially worked great when I manually inputted range of rows to search in but when indirect was introduced I am getting my error message I programmed (leading me to believe there isn't an error with my function)
When I utilize the direct references (meaning I'd have to change the date ranges directly in formula) it works great!
`=xlookup(T3,Statistics!F203:F215,Statistics!A203:215,"Not Scheduled",1,1)`
However when I use indirect to reference the specific range (so that I can adjust the date range over one cell and the reference rows recalculate) I get the error message I programmed.
`=xlookup(T3,indirect(Statistics!Q11&":"&Statistics!R11),indirect(Statistics!T11&":"&Statistics!U11),"Not Scheduled",1,1)`
Q11=F203
R11=F215
T11=A203
U11=A215
The correct answer lies in Row F205 which is within the ranges specified in the function.
Usually I figure these out or find a typo when utilizing my functions but I'll be honest and say this has me stumped. The references on the other page are identical. Just for note I also tried a Concateate function to combine the two cells before calling indirect with the same result. Any help would be greatly appreciated as this would add verification to a transportation system instead of double entry across two systems.
As far as I see it, what I think you're missing is the name of the sheet. You're referencing to the cells but not clarifying that the INDIRECT function has to look into "Statistics". Try adding it:
=xlookup(T3,indirect("Statistics!"&Statistics!Q11&":"&Statistics!R11),indirect("Statistics!"&Statistics!T11&":"&Statistics!U11),"Not Scheduled",1,1)
About that 1 after "Not Scheduled", are you sure you don't want an exact match only? Meaning to use a 0 instead of 1

How to highlight objects that repeat within 30 days by conditional format?

How can i highlight objects that appears within 30 days by conditional format in google sheet ?
Column A contain date and Column B contains objects.
(only objects need to get highlighted)
thanks in advance. here a link shared to the sheet
https://docs.google.com/spreadsheets/d/1bdWKHtW6SA6XI5pIZUrlLQJLi-Gywx2iYUqxFWY1e0I/edit#gid=0
one more sheet added ('estimated output') where you can see how the output must be. please have a look. thank you
As far as I understood, there are two conditions that must be met for a cell to be highlighted:
The object inside the cell must be repeated in that column.
A date between an object must be 30 days apart from the date of a repeated object.
This behavior can be accomplished with the following formula, which you can plug in as a custom formula in a Conditional Formatting rule:
=AND(COUNTIF(B$2:B$7,B2)>1,COUNTIF(TRANSPOSE(ARRAYFORMULA(IF(B$2:B$7=B2,ABS(A$2:A$7-A2),false))),30)>0)
The first part (COUNTIF(B$2:B$7,B2)>1) makes use of the COUNTIF formula to ensure that the object is repeated (that there are more than one instances of it). The second part uses an ARRAYFORMULA to enumerate the differences between dates with an IF clause to make sure those dates correspond to the same object, and then the COUNTIF formula is used again to check that there is at least one instance of those differences being 30. Finally, those two conditions are combined with an AND formula.

How to get only one of two repeating values

The Issue
In simple terms, I am trying to set a formula for an alternating pattern. The issue I keep running into is the fact that there are two alternating values, and Google Sheets doesn't like to repeat only one of those values without the other.
I have created an example sheet to demonstrate my issue. In Column A, a date is input. Column B and Column C then autofill with the day of the week and AM or PM respectively. Every other value in Column C alternates between AM and PM. I am trying to set it up so that the row is blank until a value in input in Column A. The issue comes when there is an odd number of Dates in Column A. Note that the alternating AM/PM pattern will never change.
What I've Tried
As seen in the image above, there are three main methods that I have tried. The data in C2:C8 is the desired result.
Method 1:
E2: =transpose(split({rept(join(";",{"AM";" "})&";",(roundup(counta(A2:A9)/2)))},";"))
F3: =transpose(split({rept(join(";",{"PM";" "})&";",(counta(A2:A9)/2))},";"))
These formulas work separately, and best represent what I am trying to accomplish, but I have not found a way to combine them to work together in one column.
Method 2:
H2: =transpose(split({rept(join(";",{"AM";"PM"})&";",(roundup(counta(A2:A9)/2)))},";"))
This is essentially the same as Method 1, but put into one formula. The issue here is that Google Sheets doesn't like to repeat half a number of times. So if the number of times to repeat (counta(A2:A9)/2) contains a half (i.e. 3.5), it will still round down to the nearest whole number.
Method 3:
J2: =ArrayFormula(TEXT(SEQUENCE(3),"")&{"AM";"PM"})
This one appeared most promising to me because when incrementing by one, it added one row, but I quickly ran into the issue where if I went over a sequence number of 2, it threw the error Array arguments to CONCAT are of different size.
References
I have used various search terms and websites to try to solve this, and have yet to find something that works. I may be missing something very simple, though, and hopefully this is a quick solution.
Example Sheet:
https://docs.google.com/spreadsheets/d/1I3EtptFLfDHpAQ8AR6Lwa01dSpJ3Cy8MTX1_OjHExSc/edit?usp=sharing
All my formulas are derived from the websites below:
REPT Function in Google Sheets
How to Repeat Multiple Columns N Times in Google Sheets
Delete everything in Col C (including the header) and place this formula in C1:
=ArrayFormula({"AM/PM"; IF(A2:A="",,IF(COUNTIFS(A2:A,A2:A,ROW(A2:A),"<="&ROW(A2:A))=1,"AM","PM"))})
The COUNTIFS finds the number of matches for the date "up to this row" for every row. Since that count will (or should) only ever be a 1 or a 2, the IF makes easy work of assigning "AM" or "PM" accordingly.
If I understand correctly it is enough to use ISEVEN function to alternate by rows:
=ArrayFormula(IF(A2:A,CHOOSE(ISEVEN(ROW(A2:A))+1,"PM","AM"),))

Is there a way to specify an input is a single cell in Google Sheets?

I want to iterate over an array of cells, in this case B5:B32, and keep the values that are equal to some reference text in a new array.
However, SPLIT nowadays accepts arrays as inputs. That means that if I use the array notation of "B5:B32" within ARRAYFORMULA or FILTER, it treats it as a range, rather than the array over which we iterate one cell at a time.
Is there a way to ensure that a particular range is the range over which we iterate, rather than the range given at once as an input?
What I considered was using alternative formulations of a cell, using INDEX(ROW(B5), COLUMN(B5)) but ROW and COLUMN also accept array values, so I'm out of ideas on how to proceed.
Example code:
ARRAYFORMULA(
INDEX(
SPLIT(B5:B32, " ", 1), 1
) = "Some text here"
)
Example sheet:
https://docs.google.com/spreadsheets/d/1H8vQqD5DFxIS-d_nBxpuwoRH34WfKIYGP9xKKLvCFkA/edit?usp=sharing
Note: In the example sheet, I can get to my desired answer if I create separate columns containing the results of the SPLIT formula. This way, I first do the desired SPLITS, and then take the values I need from that output by specifying the correct range.
Is there a way to do this without first creating an output and then taking a cell range as an input to FILTER or other similar functions?
For example in cell C35 I've already gotten the desired SPLIT and FILTER done in one go, but I'd still need to find a way to sum up the values of the first character of the second column. Doing this requires that I take the LEFT value of the second column, but for that I need to output the results and continue in a new cell. Is there a way to avoid this?
Ralph, I'm not sure if your sample sheet really reflects what you are trying to end up with, since, for example, I assume you are likely to want the total of the hours per area.
In any case, this formula extracts all of the areas, and the hours worked, and is then easy to do further calculations with.
=ArrayFormula({REGEXEXTRACT({C5:C9;D5:D9;E5:E9;F5:F9;G5:G9;H5:H9},"(.*) \d"),
VALUE(REGEXEXTRACT({C5:C9;D5:D9;E5:E9;F5:F9;G5:G9;H5:H9}," (\d+)hrs"))})
Try that in cell E13, to see the output.
The first REGEXEXTRACT pulls out all the text in front of the first space and number, and the second pulls out all the digits in a string of " #hr" in each cell. These criteria could be modified, if necessary, depending on your actual requirements. Note that it requires the use of VALUE, to convert the hours from text to numeric values, since REGEXEXTRACT produces text (string) results.
It involved concatenating your multiple data columns into one long column of data, to make it simpler to process all the cells in the same way.
This next formula will give you a sum, for whatever matching room/task you type into B6, as an example.
=ArrayFormula(QUERY({REGEXEXTRACT({C5:C9;D5:D9;E5:E9;F5:F9;G5:G9;H5:H9},"(.*) \d"),
VALUE(REGEXEXTRACT({C5:C9;D5:D9;E5:E9;F5:F9;G5:G9;H5:H9}," (\d+)hrs"))},
"select Col1, sum(Col2) where Col1='"&B6&"' group by Col1 label sum(Col2) '' ",0))
I will also answer my own question given what I know from kirkg13's answer and other sources.
Short answer: no, there isn't. If you want to do really convoluted computations with particular cell values, there are a few options and tips:
Script your own functions. You can expand INDEX to accept array inputs and thereby you can select any set of values from an array without outputting it first. Example that doesn't use REGEXMATCH and QUERY to get the SUM of hours in the question's example data set: https://docs.google.com/spreadsheets/d/1NljC-pK_Y4iYwNCWgum8B4NJioyNJKYZ86BsUX6R27Y/edit?usp=sharing.
Use QUERY. This makes your formula more convoluted quite quickly, but is still a readable and universally applicable method of selecting data, for example particular columns. In the question's initial example, QUERY can retrieve only the second column just like an adapted INDEX function would.
Format your input data more effectively. The more easily you can get numbers from your input, the less you have to obfuscate your code with REGEXMATCHES and QUERY's to do computations. Doing a SUM over a RANGE is a lot more compact of a formula than doing a VALUE of a LEFT of a QUERY of an ARRAYFORMULA of a SPLIT of a FILTER. Of course, this will depend on where you get your inputs from and if you have any say in this.
Also, depending on how many queries you will run on a given data set, it may actually be desirable to split up the formula into separate parts and output partial results to keep the code from becoming an amalgamation of 12 different queries and formulas. If the results don't need to be viewed by people, you can always choose to hide specific columns and rows.

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.

Resources