ArrayFormula & Vlookup: exclude the current row from Vlookup range - google-sheets

I'm building a simple booking system using GoogleSheets.
Each entry (Row) is a "booking request".
The intervals being entered on the sheet (via a form) are fixed to 1, 2 or 3 hours (which makes this a "simple booking system"). I'm validating these "booking requests" based on "overlaps" in Start & End Time in a couple separate columns L, M, N.
Sheet Data
The Formula
IF(ROW($A:$A)=1,
"CheckEndDateTimes (1 hour after Start)",
IF(ISERROR(VLOOKUP($E1:$E+1/24,$G$2:$G,1,FALSE)),
"",
"overlaps 1 hour ahead of start"
)
)
)
I'm using ArrayFormula, such that it auto-updates Sheet data as entries are added via a form. I'm using VLOOKUP because it seems to work well with ArrayFormula (as opposed to Index/Match).
The generalized issue:
Column E contains the search_key values (i.e. $E1:$E) (Start Datetime).
Column G contains the range (i.e. $G:$G) (End Datetimes), which I am using to compare intervals (1, 2, 3 hours) ADDED to the search_key value (Column E)
SO far this works fine, EXCEPT that the Row that is being evaluated ALWAYS evaluates to an overlap; clearly, the mere existence of the End Time in the Row being evaluated is always resulting in an overlap.
My Question
Is there a way I can EXCLUDE the current row from the range being evaluated in the VLOOKUP function. (I tried adding "<>", as follows, but it results in NO matches being found:
VLOOKUP($E1:$E+1/24,$G$2:$G<>$G1:$G,1,FALSE)
Or even (to test),
VLOOKUP($E1:$E+1/24,$G$2:$G<>$G$4,1,FALSE)
Any help would be appreciated, Thank you kindly. :)

On reflection I think you need to use countifs to check for any rows with a matching overlapped time and with a row number not equal to the current row like this:
=ArrayFormula(if(A2:A="",,countifs(C2:C,A2:A+1/24,row(C2:C),"<>"&row(A2:A))))
Then you can test whether the result is non-zero and display a message:
=ArrayFormula(if(A2:A="",,if(countifs(C2:C,A2:A+1/24,row(C2:C),"<>"&row(A2:A)),"overlaps 1 hour",)))
and similarly for 2 and 3 hours.
BTW I don't think row three does overlap at one hour.

Related

How to get last number in a row on Google Spreadsheets

Spreadsheet
So Basically i'm building a sheet to monitor my NW, my problem is simple, i need the 2023 column to adapt to whatever last number on that row there is, for income i just sum values, but for example the total amount in my bank account is different every month, i just need 2023 column to track that, i update the 31 of each month.
With the formula you can see in the screenshot i have some problem, if i input the number alright, if the value is defined by other formulas it won't show up. Any solutions?
THANKS
Only formula present in the row
Another formula from the comments that seems to not work
I tried =INDEX(X:Y;1;COUNTA(X:Y)) but it won't update if the last number in the row is generated by other formulas.
You can use this formula to find the last value of each row in the range D2:O26. Adapt it as needed!
=BYROW(D2:O26,LAMBDA(e,XLOOKUP(1,INDEX(1/(e<>"")),e,,0,-1)))
Explanation:
BYROW creates an array formula in each row of the range. To each row, here denoted as e, the specified lambda function is applied.
INDEX(1/(e<>"")) returns an array with 1 in places where cell is not empty, and #N/A for empty cells.
XLOOKUP finds the index of the last occurrence (parameter search_mode set to -1) of 1 (first parameter) in the array returned by INDEX and returns corresponding value in the row.
You haven't show what formula in columns Jan-Dec causes you problems. If formula produce values 0 in case you don't what them to count, you can use countif to filter them out:
=INDEX(D13:O13,1,COUNTIF(D13:O13,">0"))
Same as your solution, this only works if columns are filled sequentially - if there are no gaps in each row, e.g. Jan and Mar are filled while Feb is blank.
If you need a more general solution, you may go with series of nested if(isblank(). Here is an example for the first three months, to get an idea:
=INDEX(D13:O13,1,IF(ISBLANK(F13),if(ISBLANK(E13),1,2),3))

In Google Sheets, how to check if Cell A (Date) is within the Date range of Cell B and C

I have a sheet with a timeline that shows a month per row in column A and an amount in USD next to that month in column B.
I want to be able to specify amounts in column G with a start and end date for that amount in columns E and F.
What I am trying to achieve is that the values in column B are automatically calculated by looking at the start and end dates specified in columns E and F and then taking the corresponding value from column G if the date in column A falls in between the date range specified in E and F.
I have found many suggestions for similar problems online but wasn't able to get any of them to work for my specific case. Any help is very welcome
You could do it as an array formula like this:
=ArrayFormula(mmult((text(indirect("A2:A"&count(A2:A)+1),"YYMM")>=text(TRANSPOSE(indirect("`E3:E"&count(E3:E)+2)),"YYMM"))*(text(indirect("A2:A"&count(A2:A)+1),"YYMM")<=text(transpose(indirect("F3:F"&count(F3:F)+2)),"YYMM"))*transpose(indirect("G3:G"&count(G3:G)+2)),(INDIRECT("G3:G"&count(G3:G)+2)+2)^0))
The idea is to develop a 2D array where the rows are the months and the columns are the amounts for matching time periods. Then use the standard Mmult method to get the row totals of the array.
Using indirect for the ranges makes the formula longer but using full-column references would be slow as it would result in a nearly 1000 X 1000 array for a default-sized sheet.
EDIT 1
Or shorter
=ArrayFormula(mmult((text(indirect("A2:A"&count(A2:A)+1),"YYMM")>=text(TRANSPOSE(indirect("E3:E"&count(E3:E)+2)),"YYMM"))
*(text(indirect("A2:A"&count(A2:A)+1),"YYMM")<=text(transpose(indirect("F3:F"&count(F3:F)+2)),"YYMM"))
,INDIRECT("G3:G"&count(G3:G)+2)))
because you can combine the row totals step with multiplication by column G.
EDIT 2
Alternatively you could just employ a much simpler pull-down formula using SUMIFS:
=ArrayFormula(sumifs(G$3:G,eomonth(E$3:E,-1)+1,"<="&A2,F$3:F,">="&A2))
This uses Eomonth to change all the start dates to the first of the month so they can be compared to the dates in column A correctly. The formula still has to be entered as an array formula because of the Eomonth calculation.
Note
The equivalent pull-down formula to the original array formulas above would be
=ArrayFormula(sumifs(G$3:G,text(E$3:E,"YYMM"),"<="&text(A2,"YYMM"),text(F$3:F,"YYMM"),">="&text(A2,"YYMM")))
but this gives zero for all rows - the reason for this is not obvious to me at time of writing.

Formula for tabulating daily running counts of a given column

I'm trying to write a formula that gives a running count of Issues for a given day. In other words: the output should enumerate each Issue for a given date (returning blank if Issue is blank), and then start again at 1 for the first issue in a subsequent date.
I've hard-coded the expected outputs in the "desired output" column (column I):
Sample dataset is in this sheet. Key pieces:
Column B contains the date
Column E contains the T-shirt size severity of each Issue
Column F contains a numerical translation of column E
Column G contains a binary output of whether there was an Issue
In my attempt (column J), I've gotten close using
=ArrayFormula(MMULT((ROW($B3:$B)>=TRANSPOSE(ROW($B3:$B))) * EXACT($B3:$B,TRANSPOSE($B3:$B))^1, ($G3:$G)^1))
...but it's not quite what I want, as:
this repeats values instead of giving blanks (e.g. row 8, 11)
this gives 0s instead of giving blanks (e.g. row 3, 4)
See Validation (column L).
Any ideas on how to get to what I'm looking for?
Just wrap your formula in the IF function
=ArrayFormula(IF(F3:F="",,YOUR.....FORMULA))
In other words
=ArrayFormula(IF(F3:F="",,
MMULT((ROW($B3:$B)>=TRANSPOSE(ROW($B3:$B))) * EXACT($B3:$B,TRANSPOSE($B3:$B))^1, ($G3:$G)^1)
))

Google sheets Average function is not calculating correctly

I'm using Google Sheets to create a financial record.
What i'm trying to do is create a formula that takes 3 columns in my data range in to consideration. The three columns are a date, a word and a number.
The first part of the formula will check that the date is the current month (not within 30 days, but the current month). The second part will check whether the word "Yes" is present in the second column, and if those two are true, then it will take the average of column 3 for all other rows that are also completely true.
Column C is Date
Column W is Word
Column Y is Number
I've tried a number of methods, the first one was to use a average IF function, where i used a filter to check the dates, and then the word Yes in the criterion. This resulted in a number, although it was incorrect, as the formula first gathered the sequence of Yes and No's, once it had the sequence it applied it to the third column but it started from my earliest entry (not my current month). This code is below.
So alternatively i tried another method. Which was using a query function. Although i'm stuck on how to compare the month of a date to the current today() month. This gives no results, even though the current month is 8, and the dates month is also 8. I've also inputted this code below.
=AVERAGEIF(filter(W8:W800,month(C8:C800)=month(today())),"Yes",Y8:Y800)
=query(query(A8:Z800,"select month(C)+1, W, Y where W ='Yes'",0),"select Col1, Col3, Col4 where Col1 ='"&month(today())&"'",1)
results explained in background
Your nesting is a bit off. If you're using FILTER, use plain AVERAGE instead of AVERAGEIF, and make sure you're grabbing the right column to aggregate. Lastly, don't forget to wrap in IFERROR to handle your empty case.
=IFERROR(AVERAGE(FILTER(Y8:Y800, MONTH(C8:C800)=MONTH(TODAY()), W8:W800="Yes")), 0)
if you have multiple criterions you need to use AVERAGEIFS instead of AVERAGEIF
=ARRAYFORMULA(AVERAGEIFS(C2:C, B2:B, "yes", MONTH(A2:A), MONTH(TODAY())))

How to find the first cell in a row where value is not empty and check if the number is less or equal the number in other cell

I've got the following Google spreadsheet:
item have ready need1 need2 need3
A 1 2 1
B 1 2 1 1
C 2 2
etc
I want to fill ready column as follows:
find the first column in need1, ..., needN range which has a non-empty value
if the value found is less or equals the value in have column, set ready column to something cheerful (e.g. yes)
if the value found is larger than the value in have column, don't do anything
So above input, when processed should look like this:
item have ready need1 need2 need3
A 1 2 1
B 1 2 1 1
C 2 yes 2
For the first step I found a suggested solution, which did not work for me:
=INDEX( SORT( FILTER( D10:H10 , LEN( D10:H10 ) ) ,
FILTER( COLUMN( D10:H10 ) , LEN( D10:H10 ) ) , 0 ) , 1 )
(it returns #REF!) Not sure what's wrong with it or how to proceed to the next step.
Thanks in advance!
If you know how many need columns you have, or even just how many columns are on the sheet, this is quite straightforward. If not and you need to look at the entire row, you might have to redesign a bit to avoid a circular reference from the cell with the formula being part of that row.
Your second two steps are fairly simple either way - you want one of two results based on a condition, so you're going to want to use =IF. Your condition is that the 'need' number is less than or equal to the 'have' number, and you want it to say 'yes' if that's true, and nothing if it isn't. So, that gives us:
=IF(need<=have, "Yes", "")
The examples below assume your table above starts from cell A1 in the top left, and that the last column in your sheet is Z
Next we need to find 'need' and 'have'. Finding 'have' is pretty easy - it's just the number in column B.
Finding 'need' is slightly more complicated. You've got the right idea using INDEX and FILTER, but your formula seems a little overcomplicated. Basically we can use FILTER to filter out the blank values, and INDEX to find the first one that is left. First, FILTER:
The range you want to filter from is everything in the same row from column D to column Z (or whatever the final column is), and the condition you want to filter for is that those same cells are not blank. For the formula you're typing into cell C2, that gives us:
=FILTER(D2:Z2, D2:Z2<>"")
Next, INDEX: If you give INDEX an array, a row number, and a column number, it will tell you what is at that the cell where that row and column meet. As we've filtered out the blanks, we just want whatever is left in the first column of our filtered array, which gives us:
=INDEX(FILTER(D2:Z2, D2:Z2<>""), 1, 1)
Or, as we only have one row in our array, and INDEX is pretty smart, simply:
=INDEX(FILTER(D2:Z2, D2:Z2<>""), 1)
So to bring it all together, our final formula for cell C2 is:
=IF(INDEX(FILTER(D2:Z2, D2:Z2<>""), 1)<=B2, "Yes", "")
Then just drag the formula down for as many rows as you need. If your sheet is or becomes wider, just change Z to whatever your last column is.
When you don't know the size of a range, use functions row, column, rows, columns.
Simple formula
Here's an example of what you are looking:
=if(INDEX(FILTER(OFFSET(D2,,,1,COLUMNS(1:1)-column(D2)+1),OFFSET(D2,,,1,COLUMNS(1:1)-column(D2)+1)<>""),1)<=B2,"yes","")
this part of formula:
OFFSET(D2,,,1,COLUMNS(1:1)-column(D2)+1)
returns the range starting from given cell (D2) to the end of Sheet (COLUMNS(1:1)-column(D2)+1)
ArrayFormula
I suggest using ArrayFormula, it'll expand automatically:
=ARRAYFORMULA(if(REGEXEXTRACT(SUBSTITUTE(trim(transpose(query(transpose(OFFSET(D2,,,COUNTA(A2:A),COLUMNS(1:1)-column(D2)+1)),,COLUMNS(OFFSET(D2,,,COUNTA(A2:A),COLUMNS(1:1)-column(D2)+1)))))," ",", "),"\d+")*1<=OFFSET(B2,,,COUNTA(A2:A)),"yes",""))
It assumes that 'Item' column has no blank values.
The solution from #Max Makhrov works, and has the advantage of using a single formula for the whole column.
However, it assumes that all of your columns at the right from your ready column (D) will be need_ columns.
The solution from #dmusgrave also works, provided you remove the extra "=" before INDEX:
=IF(INDEX(FILTER(D2:Z2,D2:Z2<>""),1)<=B2,"Yes","").
However, it makes the same assumption, and also limits at column Z.
Such assumptions seem reasonable, but if they are limiting you, here's how you can have any number of need_ columns starting right of your ready column:
=IF(INDEX(FILTER(INDIRECT( "D"&ROW()&":"&CHAR(67+COLUMNS(FILTER($1:$1,LEFT($1:$1, 4)="need")))&row() ), INDIRECT( "D"&ROW()&":"&CHAR(67+COLUMNS(FILTER($1:$1,LEFT($1:$1,4)="need")))&row() )<>""),1)<=B2,"Yes","")
The idea is simply to replace D2:Z2 (in #dmusgrave's solution) by :
INDIRECT( "D"&ROW()&":"&CHAR(67+COLUMNS(FILTER($1:$1,LEFT($1:$1, 4)="need")))&row() )
Explanation: You start from D at current row, and you go until the last need_ column on the same current row.
CHAR(68) is D, to which you add the number of columns titled need.*, minus one (hence the 67).
Using the same logic, you can easily make your formula more robust/generic, such as not having the need_ columns starting right form the ready column, etc.

Resources