I am trying to make an automated attendance sheet
I have 2 google sheets,
the first one is the responses from a google form that has the name of the students and the date they attended, so it will have duplicated name and duplicated dates.
The second sheet have the names of the students on the left and the dates on the top.
I am trying to automate the second sheet to put "P" under the date that the student was present and "A" when his name is not in the first sheet with that date.
Best i could do was adding an extra column with the letter "p" in the first sheet and using dget to search for the name and date and output the "p" from the extra column, which only worked for one of them for some reason.
=DGET('ATTENDANCE DATE !B:D, "AT", {"NAME", "DATE"; $H$4,12})
I tried to use query also but no luck.
=QUERY('ATTENDANCE DATE'!B7:D,"
SELECT D
WHERE B MATCHES'"&$H9&"' AND C MATCHES '"&I$2&"'
")
Sorry if my question was confusing.
A good solution is to use 4 formulas to do exactly what you like. Each formula has a function:
B1 formula: generates the headers for all the dates with data.
B2 formula: generates the sub-header with the day of the week for each date.
A3 formula: gets all the names.
B3 formula: gets the attendance values for all users. This is the most complexs one.
Here is how it looks:
A
B
1
< fromula 1 >
2
name
< formula 2 >
3
< formula 3 >
< formula 4 >
Before starting there a few things to note
Questions and more information
Please, if at any point you don't understand something, let me know (I'd like this to be a nice resource on how to do formulas).
Also, at the end I left links to all the formulas I use, so you can see what they exactly do.
Locale
I'm using the English locale. this means that I'm using commas , to separate arguments (instead of ;) and array literals (instead of \). if you have function formatting errors, look into it, as this could be the issue.
Sheet names
I've changed the Sheet's names as they are very long and made the formulas harder to follow. Fell free to replace the names on the formulas back to the original name. Here is how I named them:
ATTENDANCE RESPONSES FROM GOOGLE FORM ⟶ Att
LATE/ABSENT RESPONSES FROM GOOGLE FORM ⟶ Late
Formula format
Almost all formulas require "ARRAYFORMULA" to show their full effects. I won't be adding it everywhere as it could get confusing. If you'd like to see what a formula part (doesn't have an equal sign =) does, go to a sheet and do:
=arrayformula(
<paste formula part>
)
Also, parts that are in <some name> are not literal, and represent the code named in between the brackets.
Formula 1
It can be split into 2 formulas:
Get the ordered unique dates
Add a Reason column for each date
Get the dates
The first thing you can use is UNIQUE to get only the unique ones and SORT to sort them. You also need to get them from both sheets as there could be a day that everyone is absent or another where everyone came. SORT(UNIQUE({Att!B2:B; Late!B2:B})) does most of the trick but you also get empty cells. because of that we add a filter. So together:
SORT(UNIQUE(FILTER({Att!B2:B; Late!B2:B}, {Att!B2:B; Late!B2:B}<>"")))
Adding Reason
The problem of it is that the number of column is not fixed (it grows over time). A good workaround is to concatenate the date with a separator and Reason and then split it again. This only works for columns and generates a 2 column, result. Then it can be moved into a single column by using FLATTEN.
FLATTEN(SPLIT(
<previous part>&"␟Reason",
"␟"
))
I'm using ␟ (Symbol For Unit Separator) as the separator as it indicates exactly what it is and is very-very unlikely to be included in the sheet.
If you use that you'll see that the date is shown in numbers. To Change that we'll format the date before concatenating and splitting:
FLATTEN(SPLIT(
TEXT(
<previous part>,
"dd/mmm/yyyy"
)&"␟Reason",
"␟"
))
Now we need to make it a row instead of a column. There is a function that does that: TRANSPOSE.
Complete formula 1
=ARRAYFORMULA(
TRANSPOSE(FLATTEN(SPLIT(
TEXT(
SORT(UNIQUE(FILTER({Att!B2:B; Late!B2:B}, {Att!B2:B; Late!B2:B}<>""))),
"dd/mmm/yyyy"
)&"␟Reason",
"␟"
)))
)
Formula 2
To add the day of the week we need to format the date with TEST and the format ddd, which is the short-version of the name of the day of the week (Mon, Tue, etc).
TEXT(B1:1, "ddd")
Note though that if the value formatted is already text, it will pass it. Because of that, we need to only do this for the columns with dates. One way that I found is to get the even-numbered columns. TO do that, it's a combination of COLUMN (get the number of the column), MOD (get the module), and IF:
IF(MOD(COLUMN(B1:1),2)=0, <formatted text>, "")
This does what we want but we now get "Sun" on columns that there is nothing. The reason is that empty cells are being interpreted as zeros. Because of that we need to add another condition: the cell is not empty.
IF((MOD(COLUMN(B1:1),2)=0)*(B1:1<>""), <formatted text>, "")
To do the logical and I'm using the product because the formula AND would return a single value ("eats" the passed array).
Complete
=ARRAYFORMULA(
IF(
(MOD(COLUMN(B1:1),2)=0)*(B1:1<>""),
TEXT(B1:1, "dddd"),
""
)
)
Formula 3
The third formula is the simplest and should be self-explanatory:
=ARRAYFORMULA(
SORT(UNIQUE(Att!A2:A))
)
Formula 4
This is the final formula. This formula is based on using VLOOKUP to know the value for each person and date.
Making a table to VLOOKUP into
The way of doing that is to generate a key by joining both values into a single text value and set the other values on the other columns. To prevent problems we add a separator to make sure that there are no combination that will be equal. Here is how the table to lookup into looks like:
< name >␟< date >
< status >
< reason >
< name >␟< date >
< status >
< reason >
< name >␟< date >
< status >
< reason >
⋮
⋮
⋮
The key for the first sheet is:
Att!$A$2:$A&"␟"&Att!$B$2:$B
To add the other 2 columns (Present and an empty one) we use a similar trick to Formula 1: we add a separator to split it later on. Because we already are using ␟ for the key, we need another one. In the same block there is another meant for this cases: ␞ (Symbol For Record Separator).
Att!$A$2:$A&"␟"&Att!$B$2:$B&"␞"&"Present"&"␞"&""
or joining the literal text:
Att!$A$2:$A&"␟"&Att!$B$2:$B&"␞Present␞"
This need to be filtered, as there are empty values, which we don't want. We'll use FILTER to do exactly that:
FILTER(Att!$A$2:$A&"␟"&Att!$B$2:$B&"␞Present␞", Att!$A$2:$A<>"", Att!$B$2:$B<>"")
For the second sheet, we do something similar but including the other columns:
FILTER(Late!$A$2:$A&"␟"&Late!$B$2:$B&"␞"&Late!$C$2:$C&"␞"&Late!$D$2:$D, Late!$A$2:$A<>"", Late!$B$2:$B<>"", Late!$C$2:$C<>"")
Note that I've added more conditionals.
This needs to be vertically joined. This can be done with an array literal:
{
<Attr formula>;
<Late formula>
}
Then we need to split the rows to expand into the multiple columns:
SPLIT(
{
FILTER(Att!$A$2:$A&"␟"&Att!$B$2:$B&"␞Present␞", Att!$A$2:$A<>"", Att!$B$2:$B<>"");
FILTER(Late!$A$2:$A&"␟"&Late!$B$2:$B&"␞"&Late!$C$2:$C&"␞"&Late!$D$2:$D, Late!$A$2:$A<>"", Late!$B$2:$B<>"", Late!$C$2:$C<>"")
},
"␞"
)
Using VLOOKUP
Now that we have where to lookup into, we can do it like so:
VLOOKUP(
A3:A&"␟"&C1:1,
<lookup table>,
2,
false
)
Note that the key that we are looking up is the one we generate. Also, this will get the values only below the dates (will fail otherwise).
Adding the reason
Since we know that the cells which are for the reason fail (since <name>␟Reason shouldn't exist), we can use IFERROR to detect it:
IFERROR(
<vlookup status>,
<vlookup reason>
)
The formula for reason is almost identical to the one for status. The only changes are that we lookup into the third column (instead of the second) and we look one to the left:
VLOOKUP(
A3:A&"␟"&OFFSET(C1:1, 0, -1),
<lookup table>,
3,
false
),
Using OFFSET instead of a range ensures that they have the same size.
Final error management
This formula fails when the key doesn't have name or date (which is outside the table or there that entry missing). For that case we add another IFERROR:
IFERROR(
<formula>,
""
)
Complete formula
=ARRAYFORMULA(
IFERROR(
IFERROR(
VLOOKUP(
A3:A&"␟"&C1:1,
SPLIT(
{
FILTER(Att!$A$2:$A&"␟"&Att!$B$2:$B&"␞Present␞", Att!$A$2:$A<>"", Att!$B$2:$B<>"");
FILTER(Late!$A$2:$A&"␟"&Late!$B$2:$B&"␞"&Late!$C$2:$C&"␞"&Late!$D$2:$D, Late!$A$2:$A<>"", Late!$B$2:$B<>"", Late!$C$2:$C<>"")
},
"␞"
),
2,
false
),
VLOOKUP(
A3:A&"␟"&OFFSET(C1:1, 0, -1),
SPLIT(
{
FILTER(Att!$A$2:$A&"␟"&Att!$B$2:$B&"␞Present␞", Att!$A$2:$A<>"", Att!$B$2:$B<>"");
FILTER(Late!$A$2:$A&"␟"&Late!$B$2:$B&"␞"&Late!$C$2:$C&"␞"&Late!$D$2:$D, Late!$A$2:$A<>"", Late!$B$2:$B<>"", Late!$C$2:$C<>"")
},
"␞"
),
3,
false
)
),
""
)
)
Final touches
The final result is something like this.
After that you can simply add formats to your taste. You can also add conditionals ones to more easily see the result.
References
MOD (Google Editors Help)
SPLIT (Google Editors Help)
TEXT (Google Editors Help)
IF (Google Editors Help)
IFERROR (Google Editors Help)
FILTER (Google Editors Help)
UNIQUE (Google Editors Help)
SORT (Google Editors Help)
TRANSPOSE (Google Editors Help)
FLATTEN (Google Editors Help)
ARRAYFORMULA (Google Editors Help)
VLOOKUP (Google Editors Help)
OFFSET (Google Editors Help)
I've completed a not-so-neat solution for you, starting on Row10 in the 'AUTO ATTENDANCE' sheet. It's divided into 4 parts:
The formula in cell D10 auto-populates Row10 with dates and empty cells in between:
=SPLIT(JOIN("|REASON|",SORT(UNIQUE({'ATTENDANCE RESPONSES FROM GOOGLE FORM'!$B$2:$B;'LATE/ABSENT RESPONSES FROM GOOGLE FORM'!$B$2:$B}))),"|")
Row 11 gets the day of the week from row 10 (if the cell above it contains a date:
=IF(ISDATE(D10),TEXT(D10,"dddd"),)
Cell C12 gets all unique names from both response sheets (auto-populates the name column):
=SORT(UNIQUE({'ATTENDANCE RESPONSES FROM GOOGLE FORM'!$A$2:$A;'LATE/ABSENT RESPONSES FROM GOOGLE FORM'!$A$11:$A}))
Cell D12 onwards gets the form responses and does the auto-attendance:
=IF($C12<>"", IF(ISDATE(D$1), IF(IFERROR(QUERY('ATTENDANCE RESPONSES FROM GOOGLE FORM'!$A$2:$B,"select A where A = '"&$C12&"' AND B = datetime '"&TEXT(D$1, "yyyy-mm-dd hh:mm:ss")&"'"), "noresult")=$C12, "PRESENT", IFERROR(QUERY('LATE/ABSENT RESPONSES FROM GOOGLE FORM'!$A$2:$D, "select C where A = '"&$C12&"' and B = datetime '"&TEXT(D$1, "yyyy-mm-d hh:mm:ss")&"'"), "NO RESPONSE")), IFERROR(QUERY('LATE/ABSENT RESPONSES FROM GOOGLE FORM'!$A$2:$D, "select D where A = '"&$C12&"' and B = datetime '"&TEXT(C$1, "yyyy-mm-d hh:mm:ss")&"'"), )), )
The cells with yellow background contain formulae, the ones with green background do not contain formulae but will be auto-populated as the forms get more responses. The single cell with red background (C11), you'll have to write manually ;) Hope this solves your issue!
Related
Using Zapier, when someone fills out my Wufoo application form, a line is created with some of the information, including Date Created as well as the locations selected from a checkbox field. On my second sheet, I want to be able to have a total of how many applications have come in just today for each location. I figured the easiest way to do this would be a CountIFs function, but I'm probably wrong.
https://docs.google.com/spreadsheets/d/1mIw_O6KT2QCyKeKmZ4aJ1EHzOkZ4xj49ZyjEMOCwUxA/edit#gid=0
Here is a copy of what I'm building if you'd like to experiment. I'm using =countifs(Sheet1!E2:T1000,"Buford,GA (Atlanta)",Sheet1!E2:T1000,"="&TODAY()) to try to find this, but always get 0 as the result.
try this:
You needed to specify the columns, in your case: E2:E to look up for the name, and S2:S for the date.
=countifs(Sheet1!E2:E,"Buford, GA (Atlanta)",Sheet1!S2:S,"="&TODAY())
You can also use Partial Match in order to use your headers in Sheet2 as the first criteria in your countifs:
=countifs(Sheet1!E2:E,"*"&B$1&"*",Sheet1!S2:S,"="&TODAY())
Then you had a problem of formatting in Sheet1 within the Date's Column (S): your second date in cell S3 was formatted as Date Time, as oppose to your first date in S2 which was formatted as Date only.
Note: The above suggestions are to resolve your personal attempt, otherwise they are other ways of achieving what you're looking for.
UDPATE (ALTERNATIVE SOLUTION):
you could use the following QUERY which summarize everything within a table and sort it in descending order:
= {"Names","Count";
ARRAYFORMULA(
SORT(
TRANSPOSE(
SUBSTITUTE(
QUERY(Applications!D1:T,
"Select Count(D),Count(E), Count(F), Count(G), Count(H), Count(I), Count(J),
Count(K), Count(L), Count(M), Count(N), Count(O), Count(P), Count(Q), Count(R), Count(S) where T = date '"&TEXT(today(),"yyyy-mm-dd")&"'
"),
"count", "")
),
2, 0)
)
}
Sounded so simple! I have a sheet called "Account", containing a running balance in column "G" and I have another sheet called "Performance", with a table which lists historical dates and column "D" needs to lookup the account balance on the day stated in column "A".
"Account" Sheet
"Performance" Sheet
For example, Performance!D2 should be "210,000.00".
Performance!D7 should be "110,000.00".
Performance!D9 would be "40,000.00".
To make this slightly more difficult I like to put formulas into the heading row as arrayformulas where possible, to avoid problems when copying and pasting data or adding new rows, etc.
I've tried many different possibilities and nothing has worked. I'm currently trying to make the following formula work, which is in Performance!D1.
=ARRAYFORMULA(if(row(D1:D) = 1, "Cash", VLOOKUP(A1:A, MIN('Balance'!A4:A <= A1:A), 7, 1)))
I've also tried some solutions involving MATCH(), FILTER(), VLOOKUP() and LOOKUP() but so far no cookie!
this should work:
=ARRAYFORMULA({"Cash";if(A2:A="",,VLOOKUP(A2:A, SORT('Account'!A4:G),7,TRUE))})
VLOOKUP(...,true) returns the value associated with the closest match in the first column without going over. Provided that the range into which you're doing the vlookup is sorted by the first column of that range.
To start, this is my first time posting and so please let me know if I can fix my post in any way to make it easier to answer.
I am trying to create an auto-expanding array formula
I have a sheet with my investment asset mix that including amounts of shares owned of each particular stock, and a sheet that tracks when I receive dividends. My goal is to write an automatically expanding array formula that will sum up the amount of shares that own of a stock on the date a dividend is received and return that value. I have written three different formulas that all accomplish this but none of them will auto-expand as an array.
I'm sure there are a lot of solutions I've overlooked. To boil it down, I need an expanding array formula that will sum the "Shares" column of my asset mix sheet ('Asset Mix'!D2:D, or 'AssetMixShares') conditionally. The name of the stock entered in 'Dividends'!C2:C needs to match the name of the stock in 'Asset Mix'!A2:A (or the named range 'AssetMixStocks'). It then needs to check the dates in 'Asset Mix'!C2:C (or 'AssetMixDates') against the dates in 'Dividends'!A2:A and sum all share amounts where the purchase date is less than (earlier than) the Ex-Dividend Date.
I could probably write some sort of vlookup array on the "Running Total" column -- 'Asset Mix'!E:E -- that would solve the issue, but I'm hoping to eliminate that column. I feel very strongly that what I'm trying to do should be possible without the help of a running total column -- I just don't have the knowledge.
I have tried countless functions and formulas, but the four that I currently have in my example worksheet are SUM, SUMPRODUCT, DSUM, and QUERY.
Attempt 1
SUM and IF
=ArrayFormula(SUM(IF('Asset Mix'!A:A=C2,IF('Asset Mix'!C:C<A2,'Asset Mix'!D:D))))
Attempt 2
SUMPRODUCT
=({arrayformula(SUMPRODUCT(--((AssetMixStock=(indirect("C"&ROW())))*(AssetMixDate<(indirect("A"&ROW())))),AssetMixShares))})
Attempt 3
DSUM
=DSUM('Asset Mix'!A:E,"Shares",{"Date","Stock";"<"&A2,C2})
Attempt 4
QUERY
=arrayformula(query(AssetMix,"Select sum(D) where A = '"&C2:C&"' and C < date'"&(text(year(A2:A),"0000") & "-" & text(month(A2:A),"00") & "-" & text(day(A2:A),"00"))&"' label sum(D) ''",0))
These will all work, as long as I manually drag the formula down, but I want to write some sort of formula that will auto-expand to the bottom of the Dividends sheet.
I have tried to create a Dummy sheet that has both of the relevant sheets. Please let me know if you can access it -- the link is below.
https://docs.google.com/spreadsheets/d/1wlKffma0NJ0KrlWxyX_N20y62azsGpFp3enhmjzJK1Q/edit?usp=sharing
Thanks so much for getting this far and any help you can provide!
We can focus in the first formula to understand a way to make it "self-expandable". As we see it contains references to the cells A2 and C2 in "Dividends" sheet:
=ArrayFormula(SUM(IF('Asset Mix'!A:A=C2,IF('Asset Mix'!C:C<A2,'Asset Mix'!D:D))))
Every time some data appears in these columns (A and C), the formula should work. We can control the presence of the formula by onEdit trigger, if editing is manual. Consider the code:
function onEdit(e) {
var sheet = SpreadsheetApp.getActive().getActiveSheet();
if (sheet.getName() == 'Dividends') {
var row = e.range.getRow();
for (var offset = 0; offset < e.range.getHeight(); offset++) {
sheet.getRange(3, 10).copyTo(sheet.getRange(row + offset, 10));
}
}
}
It checks any modification on the sheet "Dividends" and copies required formula to the modified row(s). This way the formula is expanded for other rows in use.
Well, it's solved! I'll leave this up in case anyone else has the same question.
A kind soul explained the magic of MMULT() to me, and wrote this solution.
=ARRAYFORMULA(MMULT((C2:C=TRANSPOSE('Asset Mix'!A2:A))*(A2:A>TRANSPOSE('Asset Mix'!C2:C)),N('Asset Mix'!D2:D))
My problem, generally stated:
I need a formula that returns all the values in a specific column for which multiple criteria in other columns of the respective row apply.
My problem, specifically stated:
I would like a formula that returns all the values in Column A for which Column C is "John", Column E is "Apples", Column G is "Earth" and both Columns H and I are empty. See here for a simplified illustration of my problem with dummy data. The correct formula, dragged down, would output a list with the values "1", "4", and "14". If you'd like to try out some stuff in the linked spreadsheet, feel free to do so in a copy of the original sheet so others can see my original data/formulas.
What I've tried so far:
Simply filtering was not an option because the data is on a separate sheet within the same spreadsheet. I also knew VLOOKUP and INDEX/MATCH were not going to do what I wanted - VLOOKUP doesn't handle multiple criteria, and while the MATCH part of INDEX/MATCH can be turned into an array to specify multiple criteria, it only returns the first value for which all conditions are true, while I need all of them.
I then tried the following formula (Formula 1 in the linked spreadsheet):
=IFERROR(INDEX($A$2:$I, SMALL(IF(COUNTIF($K$2, $C$2:$C)*COUNTIF($K$3, $E$2:$E)*COUNTIF($K$4, $G$2:$G), ROW($A$2:$I)-MIN(ROW($A$2:$I))+1), ROW(A1)), COLUMN(A1)),"")
It worked like a charm, until I wanted to include the condition that both columns H and I should be empty. I tried this, but for some reason I don't understand it didn't work (Formula 2 in the linked spreadsheet):
=IFERROR(INDEX($A$2:$I, SMALL(IF(COUNTIF($K$2, $C$2:$C)*COUNTIF($K$3, $E$2:$E)*COUNTIF($K$4, $G$2:$G)*COUNTIF($K$5, $H$2:$H)*COUNTIF($K$6, $I$2:$I), ROW($A$2:$I)-MIN(ROW($A$2:$I))+1), ROW(A1)), COLUMN(A1)),"")
Then I tried to nest my first formula into an IF/VLOOKUP (Formula 3 in the linked spreadsheet):
=IFERROR(IF(VLOOKUP(INDEX($A$2:$I, SMALL(IF(COUNTIF($K$2, $C$2:$C)*COUNTIF($K$3, $E$2:$E)*COUNTIF($K$4, $G$2:$G), ROW($A$2:$I)-MIN(ROW($A$2:$I))+1), ROW(A1)), COLUMN(A1)),$A$2:I,8,FALSE)<>"","",INDEX($A$2:$I, SMALL(IF(COUNTIF($K$2, $C$2:$C)*COUNTIF($K$3, $E$2:$E)*COUNTIF($K$4, $G$2:$G), ROW($A$2:$I)-MIN(ROW($A$2:$I))+1), ROW(A1)), COLUMN(A1))),"")
This worked if I only asked for column H to be empty, but a) it is very unwieldy, b) it gives you blanks in the list it returns, which I do not want, and c) I could not get it to work for the condition that both columns H and I need to be empty using OR.
That's where I'm stuck, and I haven't come up with a good solution. Knowing this forum, I'm sure someone has an elegant solution I was not smart enough to find :)
I'm on phone so formating will suffer.
Create a new column on the left and insert the following into cell A2
=if(D2="John", if(F2="Apples", if(H2="Earth", if(I2="", if(J2="", B2,""), "" ), "" ), "" ), "")
I've been at this problem for a while now. I am trying to sum numbers under a specific column when the rows equal a certain text and then display that sum on a different sheet. So far I came up with this formula: =IF(EXACT(A2,Table!A2:A)=TRUE,SUM(Table!C2:C)); however the only problem is that is sums everything in column C (which makes sense).
I wish there was a way to do something like the following: SUM(Table!C2:C where EXACT(A2,TABLE!A2:A)=TRUE). I've also tried the SUMIF(), DSUM(), and QUERY() functions to no avail. I must be getting logically tripped up somewhere.
Figured it out: =SUM(FILTER(Table!E4:E, EXACT(Table!A4:A,A4)=TRUE)).
=sum ( FILTER (b1:b10, a1:a10 = "Text" ) )
// the above formula will help you to take the sum of the values in column B when another column A contain a specific text.
The formula is applicable only in Google Spreadsheets