How can I use Array Formula with multiple SUMIFs? - google-sheets

I am a beginner with google sheets formulas, I created a formula which is not very efficient nevertheless it works and gives correct output, here is the formula:
=if($C$2:$C = "Check-Out",
SUM((SUMIFS($D$2:$D,$C$2:$C,"Check-In",$B$2:$B,B2,$A$2:$A,"<"&A2)+
SUMIFS($D$2:D,$C$2:$C,"Request Extra Key",$B$2:$B,B2,$A$2:$A,"<"&A2))-
(SUM(SUMIFS($D$2:$D,$C$2:$C,"Check-Out",$B$2:$B,B2,$A$2:$A,"<"&A2)+
SUMIFS($D$2:$D,$C$2:$C,"Return Extra Key",$B$2:$B,B2,$A$2:$A,"<"&A2)))),D2)
I want to copy this formula automatically to the last nonempty row, and I used ArrayFormula to copy this to last row:
=ARRAYFORMULA(if($C$2:$C = "Check-Out" ,SUM((SUMIFS($D$2:$D,$C$2:$C,
"Check-In",$B$2:$B,B2:B,$A$2:$A,"<"&A2:A)+SUMIFS($D$2:$D,$C$2:$C,
"Request Extra Key",$B$2:$B,B2:B,$A$2:$A,"<"&A2:A))-
(SUM(SUMIFS($D$2:$D,$C$2:$C,"Check-Out",$B$2:$B,B2:B,$A$2:$A,"
<"&A2:A)+SUMIFS($D$2:$D,$C$2:$C,"Return Extra Key",$B$2:$B,B2:B,$A$2:$A,"
<"&A2:A)))),$D$2:$D))
it does copy the formula to the last row but the output values are not correct, here is the sheet, Column E contains correct values by dragging down the formula. I want to get same values by using ArrayFormula. Any help would be much appreciated. Thank you

From what I'm understanding you're trying to keep track of how many keys are dispensed and whether it is permitted to dispense an additional key or not (based on rules).
For this I would take a different approach.
I would use a separate Tab (KeyCount) for calculations and only return the OK or NOT OK message back to the KeyLog Tab.
In the KeyCount Tab I would list all the Keys and the number of times they were added to the system, subtracted by check ins etc.
KeyCount Tab
| Key ID | Add Key | Check-In | Check-Out | Return Extra | Count |
For the Key ID Column I would list all the unique keys:
=unique(KeyLog!B2:B)
For Add Key I would sum all the entries that list that unique key:
=arrayformula(sumif(KeyLog!B2B:B,KeyCount!A2:A,KeyLog!D2:D))
For Check-In I would do the same as above but make it a negative by surrounding the formula with -( ). This way this will be a negative number regardless of the sum.
Do the same as above for the other Triggers.
Finally I would add a Count column which simply uses an Array to add and subtract the different triggers in each row to tell you the current balance of keys for each unique key.
This is the figure you use in your KeyLog to decide if it's OK or NOT OK.
BONUS: You can set up a table in another tab which decides if its ok or not ok. For example if number of keys should not exceed 2, then have your array formula in KeyLog look up this table table and return the OK or NOT OK Message. This way if you change policies and allow extra keys you can easily change the rules in this table without messing with the formulas in other tabs. You can also add a longer explanation message such as "Not enough keys" or "Number of allowed keys exceeded" to this table which can show in the KeyLog table.
I hope this helps you solve your problem. Let me know if you need further explaining.
Happy coding!

Related

How do I code a formula to apply to an entire column in Google sheets, even when you insert a new row?

I have a basic cash flow Google Sheet that I use to track my personal finances, cash in and out of my checking account.
Here's basically what it looks like:
Column A Column B Column C
Row 1 | Water bill | -50.00 | 400.00
Row 2 | Credit card | -300.00 | 100.00 >> the formula here is =sum(C1,B2)
Row 3 | Paycheck | 2000.00 | 2100.00 >> the formula here is =sum(C2,B3)
I project this out a full year. Anytime I want to add a row, I have to manually apply the formula in column C, and then I also have to fix the formula in the row just below it, which then fixes the problem for the rest of the sheet.
In Google Sheets, Is there a way for me to hard-code a formula for Column C that would allow me to insert a new row and always have it math perfectly without having to manually add the formula, and fix the formula in the row below it?
Let me know if there would be an easier way to do this by making fundamental changes to how it's setup - this is just how I've done it for so long, and I'm looking for a way to automate this going forward.
I've heard array formula might be helpful, but I'm not sure how to set it up.
There are many kinds of ArrayFormulas and LAMBDA functions. I suggest you look into them for different cases and uses. There is one kind in particular that would serve to your purposes:
=SCAN(0,B2:B,LAMBDA(a,v,a+v))
If you put this in C2 you'll have a cumulative sum row by row. Try it and let me know!
If you want to hide the results if column B is empty you can use another ARRAYFORMULA to check if B is empty it returns empty, either way returns the SCAN result:
=ARRAYFORMULA(IF(B2:B="","",SCAN(0,B2:B,LAMBDA(a,v,a+v))))
If you need to insert a new row in between then it's quite tedious as it will mess up the formulas below it, but if you just keep appending a new entry on the bottom row, then the formula will smartly mimic the behavior above it. Then you can simply do this:
Select the current last row, then click the small dot at the end and drag it down to as much as you want
OR, Ctrl+C (Copy) the last row, then select as much empty rows beneath it as you want, then Ctrl+V (Paste)

Return Row Data if a Name is Found in a Column

I have a table with names on the left and corresponding work schedules to the right. I've created a separate table with some of those same names and want it to automatically fill in the corresponding work schedule for that person. Seemed simple but I'm very stuck. My level of experience with Google Sheets is what is stopping me from solving this.
Example Tables:
In the attached picture the table on the top is the original (hardcoded) data. The table on the bottom is where I want the schedule data to be automatically produced based on the name on the left. The fields with #N/A and #ERROR! are both failed formulas I tried. #N/A should have returned B7:G7. #ERROR! should have returned B4:G4.
I tried the 'LOOKUP' function with ARRAYFORMULA(INDEX) hoping to have it look up the value in the column and input the work schedule data that corresponds.
=LOOKUP("Clair",A1:A9,ARRAYFORMULA(INDEX(B1:G9)))
yielded an #N/A.
Started trying to use =If(REGEXMATCH(A13:A21,"Clair"),... ...) but the '... ...' shows where my intellectual limits are at the moment. I couldn't finish it because I think it's the wrong formula to use.
Something like this maybe?
Remove everthing in B13:G17, and put this formula in B13
=BYROW(A13:A17,LAMBDA(NAME,XLOOKUP(NAME,A1:A9,B1:G9,"NOT FOUND")))
BYROW() work with an array row by row, the given data A13:A17 has only 1 column, which is the name of staff as lookup value.
Details: https://support.google.com/docs/answer/12570930?hl=en
XLOOKUP() scan an array for a key value (lookup value), and return another array with corresponding row or col index.
Details: https://support.google.com/docs/answer/12405947?hl=en
try:
=INDEX(IFNA(VLOOKUP(A13:A17; A1:G10; SEQUENCE(1; 6; 2); )))

Removing ColumnA from ColumnB, then presenting the results in ColumnC in Google Sheets

I have a Google Sheet with two columns: SetA and SetB. SetA contains values that must be removed from SetB, so that what I am left with is SetC, which contains ONLY values that are unique to SetB.
Here is a copy of the file I've been beating my head against for the past few hours:https://docs.google.com/spreadsheets/d/1Qq8EAPMTrvOiGnIS-cFk2y13KNt7nBahcbflrm-cPmg/edit#gid=0
Note that all my attempts have failed so far because Google Sheets wants to compare based on rows instead of columns. It looks at A:2, and if doesn't match B:2, it marks the value as "unique", without bothering to see if A:2's value appears elsewhere in B. If "12345" is in Row 1 of SetA, and in row 3,456 of SetB, it still needs to be removed from SetB.
Background: SetA is a list of "good" product codes. SetB is a list of ALL product codes. I need to indentify the bad codes, and the only way to do that is to remove the "Good" list from the "Master" list, which will leave me with a "Bad" list. Thanks in advance!
I tried to compare the data using =UNIQUE(A2:B54341) but all that didn't work, it just echoed the same data. I was expecting to see a column with only the values from SetB that were NOT in SetA.
Little bit simpler approach.
=UNIQUE(FILTER(B2:B,INDEX(COUNTIFS(A2:A,B2:B)=0)))
To remove blank rows from Column B.
=UNIQUE(FILTER(B2:B,B2:B<>"",INDEX(COUNTIFS(A2:A,B2:B)=0)))
You can try with this formula that finds with BYROW if there is a match in A or not:
=FILTER(unique(B2:B),BYROW(UNIQUE(B2:B),LAMBDA(each,IFERROR(MATCH(each,A2:A,0)=0,TRUE))))

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

How to assign a unique ID to a google form input?

Google Forms - I have set up a google form and I want to assign a unique id each of the completed incoming form inputs. My intention is to use the unique ID as an input for another google form I have created which I will use to link the two completed forms. Is there another easier way to do this?
I'm not a programmer but I have programming resources available to me if needed.
I was also banging my head at this and finally found a solution.
I compose a 6-digit number that gets generated automatically for every row and is composed of:
3 digits of the row number - that gives the uniqueness (you can use more if you expect more than 998 responses), concatenated with
3 digits of the timestamp converted to a number - that prevents guessing the number
Follow these instructions:
Create an additional column in the spreadsheet linked to your form, let's call it: "unique ID"
Row number 1 should be populated with column titles automatically
In row number 2, under column "Unique ID", add the following formula:
=arrayformula( if( len(A2:A), "" & text(row(A2:A) - row(A2) + 2, "000") & RIGHT(VALUE(A2:A), 3), iferror(1/0) ) )
Note: An array formula applies automatically to the entire column.
Make sure you never delete that row, even if you clear up all the results from the form
Once a new submission is populated, its "Unique ID" will appear automatically
Formula explanation:
Column A should normally hold the timestamp. If the timestamp is not empty, then this gives the row number: row(A2:A) - row(A2) + 2
Using text I trim it to a 3-digit number.
Then I concatenate it with the timestamp converted to a number using VALUE and trim it to the three right-most digits using RIGHT
Voila! A number that is both unique and hard-to-guess (as the submitter has no access to the timestamp).
If you would like more confidence, obviously you could use more digits for each of the parts.
You can apply unique ID numbers using an arrayformula next to the form data. In row 1 of the first rightmost empty column you can use something like
=arrayformula(if(row(A1:A)=1,"UNIQUE ID",if(len(A1:A)>0,98+row(A1:A),iferror(1/0))).
A few comments regarding the explanation provided by #Ying, which I will try to expand, as it is very good.
> Column A should normally hold the timestamp.
In my case, it is date+time stamp.
> 4. Make sure you never delete that row,
even if you clear up all the results from the form
That issue can easily be avoided by placing the formula in the header like this
={"calculated_id";arrayformula( if( len(C2:C); "" & text(row(C2:C) - row(C2) + 2; "000") & RIGHT(VALUE(C2:C); 3); iferror(1/0) ) )}
This formula provides an string for one cell, and a formula for the next one, which happens to be an array formula which will cover all the cells below.
Note: Depending on your language settings you may need to use ";" or "," as separator among parameters.
> 5. Once a new submission is populated,
its "Unique ID" will appear automatically
Issue
And here is the issue I see with this solution.
If the Google Form allows responders to Edit their responses, the date+time stamp will change and so the calculated_id.
A workaround is to have 2 columns, one is the calculated_id and the other will be static_id.
static_id will take whatever is on calculated_id only if itself has no data, otherwise it will stay as it is.
Doing that we will have an ID that will not change no matter how many updates the response experience.
The sort formula for static_id is
=IF(AND(IFERROR(K2)<>0;K2<>"");K2;L2)
The large one is
={"static_id";ArrayFormula(IF(AND(IFERROR(M2:M)<>0;M2:M<>"");M2:M;L2:L))
}
M or K -> static_id
L -> calculated_id
Remember to put this last one on the header of the column. I tend to change the color to purple when it has a formula behind, so I don't mess with it by mistake.
Extra info.
The numeric value from the date/time stamp differs when it comes from both or just one. Here are some examples.
Note that the number of digits on the fractional part differ quite a lot depending on the case.

Resources