Increment Number By Value With Condition - Array Formula - google-sheets

i'm confused with the following condition, simply i want to have arrayformula or maybe a custom-formula to increment number in a way bound by specific condition based on value in other column, put it simply :
if the group doesn't change and sub-group is different increment number by 1
else if the group doesn't change and sub-group is doesn't change (same) hold value by previous
else if the group change regardless sub-group value reset number back to 1
for ilustration
** notes Number is the result that i want, in example i fill it manually
Group
Sub-Group
Animal
Number
Land
poisonus
snake
1
land
friendly
dog
2
land
friendly
cat
2
land
scary
lion
3
aquatic
friendly
nemo fish
1
aquatic
predator
shark
2
UPDATE (dummy file link) :
https://docs.google.com/spreadsheets/d/1DAPf-DvWz50_DJ0IqAoSHbfEnfg_mN1lNXHcCjkj27M/edit#gid=0

try:
=INDEX(IF(A4:A="",,VLOOKUP(A4:A&B4:B, {UNIQUE(A4:A&B4:B), COUNTIFS(
REGEXEXTRACT(UNIQUE(A4:A&"×"&B4:B), "(.*)×"),
REGEXEXTRACT(UNIQUE(A4:A&"×"&B4:B), "(.*)×"),
SEQUENCE(COUNTA(UNIQUE(A4:A&"×"&B4:B))), "<="&
SEQUENCE(COUNTA(UNIQUE(A4:A&"×"&B4:B))))}, 2, 0)))

I have entered my solution in cell D1 of the sheet "Erik Help." As I said in the comments to your original post, this is a more complex solution than I can generally offer here on the free, volunteer-run forums. I did choose to develop and share the formula with you, but I will need to leave it to you (and any other future site visitors who may be interested) to study the formula for understanding how it works. Explaining the formula would take longer than writing it.
Here is the formula:
=ArrayFormula({"Number"; IF(A2:A="",,VLOOKUP(LOWER(A2:A&B2:B),QUERY({UNIQUE(FILTER({A2:B,A2:A&B2:B},A2:A<>"")),COUNTIFS(QUERY(UNIQUE(FILTER({A2:B,A2:A&B2:B},A2:A<>"")),"Select Col1"),QUERY(UNIQUE(FILTER({A2:B,A2:A&B2:B},A2:A<>"")),"Select Col1"),SEQUENCE(COUNTA(QUERY(UNIQUE(FILTER({A2:B,A2:A&B2:B},A2:A<>"")),"Select Col1"))),"<="&SEQUENCE(COUNTA(QUERY(UNIQUE(FILTER({A2:B,A2:A&B2:B},A2:A<>"")),"Select Col1"))))},"Select Col3, Col4"),2,FALSE))})

Related

How to check for overlapping dates

I am looking for a solution on either Google sheets or app script to check for overlapping dates for the same account. There will be multiple accounts and the dates won't be in any particular order. Here is an example below. I am trying to achieve the right column "check" with some formula or automation. Any suggestions would be greatly appreciated.
Start Date
End Date
Account No.
Check
2023-01-01
2023-01-02
123
ERROR
2023-01-02
2023-01-05
123
ERROR
2023-02-25
2023-02-27
456
OK
2023-01-11
2023-01-12
456
OK
2023-01-01
2023-01-15
789
ERROR
2023-01-04
2023-01-07
789
ERROR
2023-01-01
2023-01-10
012
OK
2023-01-15
2023-01-20
012
OK
I also found some similar past questions, but they don't have the "for the same account" component and/or requires some sort of chronological order, which my sheet will not have.
How to calculate the overlap between some Google Sheet time frames?
How to check if any of the time ranges overlap with each other in Google Sheets
Another approach (to be entered in D2):
=arrayformula(lambda(last_row,
lambda(acc_no,start_date,end_date,
if(isnumber(match(acc_no,unique(query(query(split(flatten(acc_no&"|"&split(map(start_date,end_date,lambda(start_date,end_date,join("|",sequence(1,end_date-(start_date-1),start_date)))),"|")),"|"),"select Col1,count(Col2) where Col2 is not null group by Col1,Col2",0),"select Col1 where Col2>1",1)),0)),"ERROR","OK"))(
C2:index(C2:C,last_row),A2:index(A2:A,last_row),B2:index(B2:B,last_row)))(
counta(A2:A)))
Briefly, we are creating a sequence of dateserial numbers between the start & end dates for each row, doing some string manipulation to turn it into a table of account number against each date, then QUERYing it to get each account number which has dateserials with count>1 (i.e. overlaps), using UNIQUE to get the distinct list of those account numbers, then finally matching this list against the original list of account numbers to give the ERROR/OK output.
(1) Here is one way, considering each case which could result in an overlap separately:
=ArrayFormula(if(A2:A="",,
if((countifs(A2:A,"<="&A2:A,B2:B,">="&A2:A,C2:C,C2:C,row(A2:A),"<>"&row(A2:A))
+countifs(A2:A,"<="&B2:B,B2:B,">="&B2:B,C2:C,C2:C,row(A2:A),"<>"&row(A2:A))
+countifs(A2:A,">="&A2:A,B2:B,"<="&B2:B,C2:C,C2:C,row(A2:A),"<>"&row(A2:A))
)>0,"ERROR","OK")
)
)
(2) Here is the method using the Overlap formula
min(end1,end2)-max(start1,start2)+1
which results in
=ArrayFormula(if(byrow(A2:index(C:C,counta(A:A)),lambda(r,sum(text(if(index(r,2)<B2:B,index(r,2),B2:B)-if(index(r,1)>A2:A,index(r,1),A2:A)+1,"0;\0;\0")*(C2:C=index(r,3))*(row(A2:A)<>row(r)))))>0,"ERROR","OK"))
(3) Most efficient is to use the original method of comparing previous and next dates, but then you need to sort and sort back like this:
=lambda(data,sort(map(sequence(rows(data)),lambda(c,if(if(c=1,0,(index(data,c-1,2)>=index(data,c,1))*(index(data,c-1,3)=index(data,c,3)))+if(c=rows(data),0,(index(data,c+1,1)<=index(data,c,2))*(index(data,c+1,3)=index(data,c,3)))>0,"ERROR","OK"))),index(data,0,4),1))(SORT(filter({A2:C,row(A2:A)},A2:A<>""),3,1,1,1))
HOWEVER, this only checks for local overlaps. not globally. You can see what I mean if you change the dataset slightly:
Clearly the first and third pair of dates have an overlap but G4 contains "OK". This is because each pair of dates is only checked against the adjacent pairs of dates. This also applies to the original reference cited by OP - here's an example where it would give a similar result:
The formula posted by #The God of Biscuits gives the correct (global) result :-)

How To Determine Match from Multiple Cells in One Row

Editable Test Sheet:
https://docs.google.com/spreadsheets/d/1zKtE09TB-mAEQFRswity3R1ZGdYe7jq6ZYh_l4P398E/edit?usp=sharing
Although I believe what I'm trying to do is fairly simple, It is difficult for me to even find the correct words to describe what I'm trying to do. I suspect that is why I've been researching all day for an answer and cannot find it.
I need an ARRAYFORMULA that can check for the existence of a given "PARENT ID" in another table, but only if another cell in the same row is not true.
A given PARENT ID from TABLE 2 could appear in TABLE 1 multiple times, sometimes paired with "TRUE" and sometimes not. I only need to know if the PARENT ID from TABLE 1 appears in TABLE 2 along with the value TRUE in the "Done" column next to it. If it appears even once, I want to denote it in TABLE 2.
I am able to do this in various forms, but none of them work with ARRAYFORMULA.
See the shared example sheet above. I would greatly appreciate any help.
See my two newly added sheets ("Erik Help" and "Erik Help 2"). Below are the formulas I used:
In "Erik Help":
=ArrayFormula({"In Table 1 and Not Done?";IF(E3:E="",,IF(ISERROR(VLOOKUP(E3:E,TRIM(B3:B&C3:C),1,FALSE)),,TRUE))})
This delivers the results you want as asked (though your manually entered results did not list F3 as TRUE when I believe it should be).
The trick here is looking for the Parent ID within a virtual column that concatenates Col B and Col C and then TRIMs out extra characters (such as null). If there is an exact match, we know that there was no value in Col B. And since the only valid Col-B value is Boolean TRUE, we are assured that any match is in effect false (or not Done).
In "Erik Help 2":
=ArrayFormula({"In Table 1 and Not Done?";IF(E3:E="",,IF(ISERROR(VLOOKUP(E3:E,TRIM(B3:B&C3:C),1,FALSE)),,IFERROR("Item ID(s): "&VLOOKUP(E3:E,REGEXREPLACE(TRIM(SPLIT(FLATTEN(QUERY(QUERY({FILTER(C3:C,A3:A<>"",B3:B<>TRUE)&"~",FILTER(A3:A&" (R"&ROW(A3:A)&")",A3:A<>"",B3:B<>TRUE)&","}, "Select MAX(Col2) GROUP BY Col2 PIVOT Col1"),, 9^9)),"~")),"[,\s]+$",""),2,FALSE),TRUE)))})
Instead of simply TRUE, this version returns the Item ID(s) that are not done for that Parent ID along with the row number where each incomplete Item ID is found.
Since you didn't expressly ask for this, I'm considering it bonus material and will not take time to explain it.
Another answer I received on another forum:
=ArrayFormula(IF(COUNTIF(C3:C&B3:B,E3:E),true,))
Credit to Prashanth KV

Correct Way To "COUNTUNIQUE" That Only Counts Once

I am trying to count the unique values of a column, based on their status in another column, example:
Customers
License Active
Adam
Yes
Barry
No
Adam
No
Claire
No
In this situation, I want to know how many customers have at least 1 active license, and how many customers do not have at least one active license.
The formula I have tried is:
=COUNTUNIQUEIFS(A2:A,B2:B,"Yes")
This returns 1 in this situation which is correct, as there is 1 customer who has a Yes on column B.
My issue is when I try to do the reverse, count the "No" using this formula:
=COUNTUNIQUEIFS(A2:A,B2:B,"No") it returns 3 which is not the desired result as it is counting the second Adam as a unique value too because they have a "No" in column B.
The result I want here is 2, because Adam has a yes somewhere in column B so I don't want him counted again the next time his field is counted.
It seems to me that the easiest way to get the "No" count is like this:
=COUNTUNIQUE(A2:A)-COUNTUNIQUEIFS(A2:A,B2:B,"Yes")
It's even easier if you've already pulled the "Yes" count to a cell (say, C2), in which case the "No" count could be gained quite simply with this:
=COUNTUNIQUE(A2:A)-C2
I don't think you can do it in a single step - try filtering out those with at least one "Yes" like this:
=countunique(filter(A2:A,countifs(B2:B,"Yes",A2:A,A2:A)=0))
Explanation
When a countifs has a range instead of a single value in its criteria part countifs(B2:B,"Yes",A2:A,A2:A) , the countifs gets re-evaluated for each cell in the range. So you get an array with the results of
countifs(B2:B,"Yes",A2:A,A2)
countifs(B2:B,"Yes",A2:A,A3)
countifs(B2:B,"Yes",A2:A,A4)
countifs(B2:B,"Yes",A2:A,A5)
and so on all the way down the columns.
The first countifs above checks right through a2:a and b2:b to see if there are any cases where the name is Adam and the license condition is true and gets a count of 1 so that row is filtered out. The same thing happens in the next row containing Adam (row 4) - the countifs checks right through both columns excluding the headers and the count is still 1 so that row is filtered out as well leaving just Barry and Claire.
If you wanted to exclude all records containing "Test" in the Customer column, You could add a condition to the filter using the multiplication operator to 'AND' it with the existing condition:
=countunique(filter(A2:A,(countifs(B2:B,"Yes",A2:A,A2:A)=0)*(A2:A<>"Test")))
If you had several names to exclude, you would probably want to make a list of them and use a lookup to stop the formula getting too long and unwieldy, but it would be the same idea.

Checkbox (or SOMETHING) to activate a row in Google Sheets

My wife runs a dance school, and occasionally needs to calculate the average age on a given date of a group of dancers. I'm not having a problem with the age calculation and averaging, but I wish to add a feature:
My sheet has all dancers in her company listed. Currently, we copy them all, paste to another sheet, and then delete the ones not included. That's a PITA, so instead I'd like to be able to put a checkbox in the first column, that when checked, would INCLUDE the associated age column in the calculation. So, she could just go down the list, click the included dancers, and it would calculate the average JUST for the selected ones and ignore everybody else.
Honestly, at this point, I have ZERO idea of where to start to do this and need a gentle push in the correct direction. Assume I'm an idiot and know almost nothing.
Here's an example sheet with the new checkbox feature to illustrate the function:
https://docs.google.com/spreadsheets/d/1G8LJyS10yi1HIa2MNHCbWUJPso9QAit3i0cO8A-Uw3A/edit?usp=sharing
I placed the formula above the "Age" column (Column C), and the Checkboxes in Column A:
=iferror(AVERAGEIF(A3:A,TRUE,C3:C),"NO DANCER'S SELECTED!")
This also displays an error message in case no dancer's are selected.
Try this. It looks for 'y' in column A. Assumes names are in column B and ages in column C. You can adjust the columns to match you sheet, and change the 'y' to whatever value you want to enter. It will average the ages of the rows with 'y' in column A:
=AVERAGEIF(A2:A,"=y",C2:C)

Count selected elements for each line and create an arrayformula that groups by number of counts

We have asked users:
What to do with the money?
[ ] paint the bridge
[ ] rebuild the school
[ ] keep the money
[ ] Other : [____________________]
Here is the spreadsheet with their answers:
A B
1 Name Choices
2 Lilia paint the bridge, rebuild the school, keep the money
3 Paul rebuild the school, paint the bridge, do something else
4 Margerite keep the money, I don't know, do what you want
5 John paint the bridge
...
800
I want a formula that output the number of official choices (excluding other) picked per user.
With the first 4 rows of data, the formula would output this table:
D E
Nbr of choices a user made Frequency (Nbr of users who made these choices)
0 0
1 2
2 1
3 1
Couldn't find a way to get this right from a single formula. For a starter, I wanted to split each line (of B2: B) by "," but couldn't find a way to apply a fn (split) to each line in an formula...
Even with 800 rows of data (B2:B), the resulting table (D2:E5) would always be 4 rows long plus titles (and two column wide)
I could do this in C2, and replicate manually with the "+" corner icon...
=countif(B2;"*rebuild the school*")+countif(B2;"*keep the money*")+countif(B2;"*paint the bridge*")
And then do in E2:
=arrayformula(countif(C2:C;D2:D5))
But I'd like to generate the table of frequencies in one formula, without any manual action (without C column).
So I am looking for a way to "map" the first function to each row, put this in the second fn.
ANSWER by Akshin Jalilov EXPLAINED
This is the answer by Akshin Jalilov, but shorter (and with international notations)
=ARRAYFORMULA(COUNTIF(ARRAYFORMULA(IF(B2:B="";;COUNTIF(ARRAYFORMULA
(IFERROR(IF(FIND("paint the bridge";B2:B);Row(B2:B);0)));"="&row(B2:B))
+COUNTIF(ARRAYFORMULA(IFERROR(IF(FIND(
"rebuild the school";B2:B);Row(B2:B);0)));"="&row(B2:B))
+COUNTIF(ARRAYFORMULA(IFERROR(IF(FIND(
"keep the money";B2:B);Row(B2:B);0)));"="&row(B2:B))));"="&D2:D5))
Step1:
IF(FIND("rebuild the school";B2:B);Row(B2:B);0)
This means, for each row (B2:B) find "rebuild the school". If you find it, return the number of the row, otherwise, return 0.
Step2:
=ARRAYFORMULA(IFERROR(Step1))
Wrap this in an ARRAYFORMULA so that you return the results for each row.
I think IFERROR is there to prevent an error from stopping the process.
Step3:
=ARRAYFORMULA(IF(B2:B="";;COUNTIF(ARRAYFORMULA(IFERROR(IF(FIND("paint the bridge";B2:B);Row(B2:B);0)));"="&row(B2:B))+countif(Step2)+countif(ARRAYFORMULA(IFERROR(IF(FIND("keep the money";B2:B);Row(B2:B);0)));"="&row(B2:B))))
This will count valid votes made by each users. This is equivalent to C2 formula referred in my manual process. But is it now part of a single global formula.
Step4:
Lastly, the rest of the formula counts frequencies of each voting count possibilities.
I know this formula is large but this is the closest I got to what you want.
Now to make it easy, name your responses range "Responses". I assume it is B2:B.
Here is the formula:
=ARRAYFORMULA(Countif(ARRAYFORMULA(IF(Responses="",,COUNTIF(VLOOKUP(row(Responses),({ARRAYFORMULA(Row(Responses)),ARRAYFORMULA(IFERROR(IF(FIND("paint the bridge",Responses),Row(Responses),0))),ARRAYFORMULA(IFERROR(IF(FIND("rebuild the school",Responses),Row(Responses),0))),ARRAYFORMULA(IFERROR(IF(FIND("keep the money",Responses),Row(Responses),0)))}),2),"="&row(Responses))+COUNTIF(VLOOKUP(row(Responses),({ARRAYFORMULA(Row(Responses)),ARRAYFORMULA(IFERROR(IF(FIND("paint the bridge",Responses),Row(Responses),0))),ARRAYFORMULA(IFERROR(IF(FIND("rebuild the school",Responses),Row(Responses),0))),ARRAYFORMULA(IFERROR(IF(FIND("keep the money",Responses),Row(Responses),0)))}),3),"="&row(Responses))+COUNTIF(VLOOKUP(row(Responses),({ARRAYFORMULA(Row(Responses)),ARRAYFORMULA(IFERROR(IF(FIND("paint the bridge",Responses),Row(Responses),0))),ARRAYFORMULA(IFERROR(IF(FIND("rebuild the school",Responses),Row(Responses),0))),ARRAYFORMULA(IFERROR(IF(FIND("keep the money",Responses),Row(Responses),0)))}),4),"="&row(Responses)))),"="&D2:D5))
Here is an example if how it works. I am not sure which one exactly you wanted so added both

Resources