Merge multiple checkbox responses in google forms response sheet - google-apps-script

I have a google Form that for various reasons has multiple checkbox questions. I eventually want a known quantity of these to be merged.
Once a form submitted, I figured I could have an app script take the submitted data, copy it to another sheet and then merge the aforementioned question responses into a single cell csv.
so take the following example:
name: 'name'
question1: ['a','b','e']
question2: []
question3: ['dog','cat']
question4: ['notMerged']
and copy it into another page that looks like:
name: 'name'
questions: ['a','b','e','dog','cat']
question4: ['notMerged']
I've been reading on about setting up the trigger for onFormSubmit and getting the data but am not sure about things like merging the results, placing it in the same row # etc. The column index of the fields to merge are static however no there will always be some amount of them that are empty so I will need to do a null check on each before joining.
I am working my way through the 'Quickstart: Managing Responses for Google Forms' code to see whats going on but I dont really know JavaScript.
Not looking for someone to write this out for me but help with a few pointers to get me goin in the correct direction

If you don't mind a slightly different format on your second sheet, you can do this without script. On the second sheet, enter your headers ('Questions' and 'Question 4' in A1 and B1. Then in A2 enter:
= IFERROR(JOIN( "," , FILTER('Form Responses 1'!B2:D2, NOT('Form Responses 1'!B2:D2 = "") )))
In B2 enter:
=iferror('Form Responses 1'!E2)
Copy the formulas down. Rows with no data (yet) will be blank until form data is added.
I figured out what is happening. When the form is submitted, it inserts a line and that causes the formulas to change. Use this code and it will set the formulas on the correct row. (Remove the old formulas.) Let me know if you have any problrms.
//you need to set an installable onFormSubmit trigger
function test(){
var ss=SpreadsheetApp.getActiveSpreadsheet()
Utilities.sleep(4000)
var s=ss.getSheets()[1]
var s1=ss.getSheets()[0]
var lr=s.getLastRow()+1
var lr1=s1.getLastRow()
var r="A"+lr1
var q="B"+lr1
var t="D"+lr1
var u="E"+lr1
s.getRange(lr, 1, 1, 1).setFormula("=iferror('Form Responses 1'!"+ r +")")
s.getRange(lr, 2, 1, 1).setFormula("= IFERROR(JOIN(\",\" , FILTER('Form Responses 1'!"+ q +":"+ t +", NOT('Form Responses 1'!"+ q +":"+ t +" =\"\") )))")
s.getRange(lr, 3, 1, 1).setFormula("iferror('Form Responses 1'!"+ u +")")
}

Related

Google Sheets looking for a google script like VLOOKUP but combines the found values and joins them into a specific cell

I have a spreadsheet where I'm trying to add an automatic search function whenever a cell on the Sheet "List" Column 1 gets selected.
It's sort of like using a VLOOKUP function of the sheets but I've been unsuccessful trying to come up with an effective code for it.
Test Spreadsheet link
I made a simple test spreadsheet hopefully for people to easily understand how the sheet should work.
In the 'Database' sheet, there's a list of people with the fruits they like or dislike.
In the 'List' sheet, the list is reversed where the fruits are now the main list but here, when a person clicks on the cells with the fruits, the top cells should automatically update with the combined names of people who like or dislike that certain fruit.
The function should go like this:
If I click a cell on column 1 of the 'List' sheet,
A1 cell updates it's value with the active cell value
B1 cell updates it's value with the combined values based on who likes it from the 'Database' sheet Column 2.
B2 cell updates it's value similar as above but from Dislikes in Column 3
I've attached the code I currently have but I'm not able to get the last 2 steps working.
There is also a guide in the sheets on what the correct answers should look like for each fruit.
I got stuck looking for a way to make the steps 2 and 3 work. I was able to find a code here but it stops after finding one match. stackoverflow.com/questions/10838294/… My current problem is: Since the cells [B2:B] to filter from on the Database sheet has multiple values, is it possible to find all cells with at least a partial match, get the values of the cells on the left of that then list them into the B1 cell on the "List" sheet?
Test Spreadsheet link
Code.gs
function onSelectionChange(e){
var currentsheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var currentcell = currentsheet.getActiveCell();
var activerow = currentcell.getRow();
var activecol = currentcell.getColumn();
var sname = currentsheet.getName();
// Check if current sheet name is correct
if (
sname == 'List'
)
{
// Check if this on first column
if (activecol == 1)
{
// ACTION
var name = currentcell.getValue(); //Get current cell for searching
var prefsheet = SpreadsheetApp.getActive().getSheetByName("Database");
var last = prefsheet.getLastRow();
var data = prefsheet.getRange(1,1,last,2).getValues(); // create an array of data from columns A and B
for(nn=0;nn<data.length;++nn){
if (data[nn][1]==name){break} ;
SpreadsheetApp.getActiveSheet().getRange('A1').setValue('Fruit: ' + name);
SpreadsheetApp.getActiveSheet().getRange('B1').setValue('Like: ' + data[nn][0]);
}
}
};
};
I was able to find a workaround for this and it turns out I didn't have to use a Google Script code to keep updating the formula on List!B1.
I'm new with scripting and sheets but I'll be sharing this formula here in case someone needs something like this in the future.
This is what I used on List!B1
=JOIN(", ",query(filter(Database!A2:B,REGEXMATCH(Database!B2:B,A1)),"Select Col1"))
Regexmatch does the search for partial value and returns as "TRUE"
Filter will then list these rows and query "Select Col1" will only keep the first column of the results and remove the second column. Lastly, Join formula will concatenate them into one cell and add separators.
Which is working very nicely for my purpose. If anyone has any suggestions I'd love to hear about it too.

Google Sheets: Action Based on Birthday

I'm trying to send myself either an email or copy the row to a new sheet when it's someone's birthday or hire date anniversary. Copying the line to a new sheet would allow me to use zapier to notify me of the update. Either would work. The sheet uses a form to collect data.
I've built a few scripts but nothing that had to do with dates. I'm just struggling with this one and have tried a few examples I could find with no luck.
Here is this sheet. It's view only so just let me know if you need more access.
I understand that you want to replicate your form responses Sheet in another Sheet (let's call it Zapier Sheet) automatically each time that a new form response is added. You can achieve that goal developing an Apps Script code that runs at each form response. In that case you can use a code similar to this one:
function so62400514() {
var formSheet = SpreadsheetApp.openById(
'{FORM SHEET ID}').getSheets()[0];
var zapierSheet = SpreadsheetApp.openById(
'{ZAPIER SHEET ID}').getSheets()[0];
var formData = formSheet.getRange(1, 1, formSheet.getLastRow(), formSheet
.getLastColumn()).getValues();
var zapierData = zapierSheet.getRange(1, 1, zapierSheet.getLastRow(),
formSheet.getLastColumn()).getValues();
var recorded = false;
for (var fr = 0; fr < formData.length; fr++) {
for (var zr = 0; zr < zapierData.length; zr++) {
if (formData[fr].toLocaleString() == zapierData[zr].toLocaleString()) {
recorded = true;
}
}
if (recorded == false) {
zapierSheet.appendRow(formData[fr]);
} else {
recorded = false;
}
}
}
This code will first open both sheets (using SpreadsheetApp.openById() and Spreadsheet.getSheets()) to select the data with Sheet.getRange (setting boundaries with Sheet.getLastRow() and Sheet.getLastColumn()) and reading it using Range.getValues(). After that operation the data will get iterated using the property Array.length as the perimeter. The iteration compares each row from the form Sheet to every row of the zapier sheet (to accomplish that, I first parsed the row as a string with Date.toLocaleString()). If the form row is found in the zapier sheet, the boolean recorded will flag to true. After every row on the zapier sheet gets compared to the form row, the code will write it down on the zapier sheet based on the boolean flag.
As explained in the previous paragraph, this code will take the form sheet rows not present in the zapier sheet; and paste them on the zapier sheet. I used this approach to prevent missing any row (as it could happen when simultaneous users answer the form all at once). To make this fire automatically you'll need to set up an installable trigger with these settings:
As an example, let's say that we have these form responses:
And our initial sample zapier sheet looks like this one below. Please, notice how several past rows are missing;
After running the script (as it will do automatically) this would be the result:
I suggest running the script manually for an initial setup. If the timestamps diverge, please check if both spreadsheets share time zones. Don't hesitate to ask me further questions to clarify my answer.

Append data on Google Sheets script editor

I have a script where a user inputs some information on the user tab. Then, they hit a button which triggers a script to dump the information they input into a separate tab which I'm calling the repository. This code is working flawlessly:
function dumpInputsToRepository(button) {
var userSheet = SpreadsheetApp.getActiveSpreadsheet();
var userTab = userSheet.getSheetByName('PhoneSelect');
var firstRow = 6;
var range = userTab.getRange(firstRow, 1, userTab.getLastRow() - firstRow +1, 10);
var data = range.getValues();
var repTab = userSheet.getSheetByName('Rep');
repTab.getRange(repTab.getLastRow() + 1, 1, data.length, 10).setValues(data);
In the repository list, I want to add two columns of data on the right side of what is being input. Specifically, I want a column which produces the timestamp of when the button was pushed, and I want another column which produces the email address of the person who pushed the button.
For the timestamp, I had the idea to add an extra column in the user tab with =Now() function, and then just extend the inputs to 11 columns of data being moved to the repository. That works, but I figure there is a more elegant solution which I could input.
Getting the email to be produced in the final column has eluded me. I've tried to rework some code from here: https://developers.google.com/apps-script/reference/base/session#getActiveUser()
But I can't get my email address to appear anywhere let alone appended to to the data that is being sent from the user sheet to repository sheet. Any help is greatly appreciated!

google sheets form function not updating with data input from the script

I'm having some trouble getting a function in a google sheet to calculate using values inserted by a script.
I have a google script that is adding data to a sheet based on user-inputted data from a form that the script has created. So, in the form, a user inputs their name, selects a product and some options, and the script adds this to a sheet named 'Client Data Sheet'.
I then have a different sheet which is supposed to do the math to calculate the price. My script copies all the functions from a hidden template row into the next-available row, so, for example, cell D7 contains ='Client Data Sheet'!A4, D8 contains ='Client Data Sheet'!B4 etc... These all display the correct values.
The problem is the price calculation function, in that same calculation sheet, which has a rather complex function that a previous coder wrote. This function should calculate the price of the product and options based on the data in the same sheet. It does so without calling a script, just pulling data from cells in other sheets, running some if() checks to decide whether or not to add extra costs to the total price, and adding it all up.
Problem is, it doesn't update based on the new data. It just shows 0, as though the other cells were empty, even though they now contain data updated by pulling data from another sheet which was edited by a script. If I go in to edit the function and just press enter, it re-calculates correctly, so I basically just need the function to re-evaluate based on the new data that is in the cells it's dependent on.
My theory is that it's not updating the function since I didn't directly edit the cells it's dependent on. I could try to change the awkward, huge function this other coder wrote so it pulls from the spreadsheet my script edits, rather than from cells that copy in that data, but that seems like a workaround, and is unsatisfying.
TL;DR: a function isn't updating based on data that changes in cells that copy the data from other cells which are filled by a script. Anybody have any advice for how to get the function to update?
EDIT: Ok, so if I make sure that the function that isn't updating pulls at least some data from cells that the script updates, it works. It seems that it doesn't recognize that the cell once removed has updated as well. It would be better, though if it was able to pull from the cells that pull their data from the cells the script updates. This would let other users of the sheet change the products if a customer requested a change later on without having to edit my hidden sheets that the data is pulled from.
Code:
This copies a template row in my spreadsheet that contains the math functions:
function new_client(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Liquidation');
if(ss.getName()=="Data" || ss.getName().indexOf("Liq")==0){
var last_row = ss.getLastRow();
var last_col = ss.getLastColumn();
ss.insertRowsAfter(last_row, 1);
var template_row = 4;
var copy_range = ss.getRange(template_row, 1, 1, last_col);
var paste_range = ss.getRange(last_row+1, 1, 1, last_col);
copy_range.copyTo(paste_range, {contentsOnly:false});
paste_range.getCell(1,1).setValue("NO");
SpreadsheetApp.flush();
}
}
This copies data from the form into a different spreadsheet, which the sheet edited above pulls data from:
var clientSheet = SpreadsheetApp.getActive().getSheetByName('Client Data Sheet');
clientSheet.insertRowsAfter(clientSheet.getMaxRows(), 1)
var lastRow = clientSheet.getLastRow() + 1;
var lastColumn = clientSheet.getLastColumn();
var destRow = clientSheet.getRange(lastRow, 1, 1, lastColumn);
var column = 1;
for (var key in user) {
if (user.hasOwnProperty(key)) {
destRow.getCell(1, column).setValue(user[key]);
column++;
}
}
So, for example, after this code is run, the Client Data Sheet contains a cell, B4, which now contains the name of the chosen product, "foo". The Liquidation sheet has a cell let's call it A5 that contains the function ='Client Data Sheet'!B4, as well as another cell which has the price calculation function: =if(A5="foo", 100, 0)
When the script above inserts the values from the form, the cell B4 in Client Sheet and the cell A5 in the Liquidation sheet will contain the right value, but the cell with the calculation function =if(B4="foo", 100, 0) will not update.

Script to copy and sort form submission data

I'm using Google forms to create a spreadsheet that I want sorted automatically by datestamp Z-A. The sorting will be triggered whenever anyone fills out a form.
I think the way to do it is:
ask if there is a "copy of Form responses" on spreadsheet...
if yes, clear all contents...
else...
copy spreadsheet to "copy of form responses"...
sort according to timestamp
Below is what I've cobbled so far. It works only the first time a response is recorded. I'm not a coder so any help is appreciated. If someone could also point me to a list of commands with basic syntax I'd be grateful.
function CopySheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var msheet = ss.getSheetByName("Form Responses");
msheet.copyTo(ss);
var CopySheet = ss.getSheetByName("Copy of Form Responses");
CopySheet.sort(1, false); // here 1 is for column no. 1 that
// is "Column A" and true is for ascending, make it
// false if you want descending.
};
You can accomplish this without a script, by using QUERY() in the copy sheet. For instance, if you put this function in cell A1 of your copy sheet, and substitute the key for your form response spreadsheet, you'll end up with a reverse-timestamp-sorted copy of the responses:
=Query(ImportRange(spreadsheet_key,"Form Responses!A:Z"), "select * order by Col1 desc")
This data will be refreshed periodically (~5 mins), so it will reflect new form submissions, but not in real-time.
Note: When using ImportRange() as source data for Query, you need to refer to columns in the Query string using ColN notation.
Alternatively, you could produce a form submission trigger function in the spreadsheet receiving the form submissions, and have it copy the sorted form responses to your copy sheet. The following function does that. You need to set it up as a trigger function for Spreadsheet Form Submission Events. For information on how to test such a function, see How can I test a trigger function in GAS?.
function copyFormSubmissions(e) {
var sourceSheet = e.range.getSheet();
var data = sourceSheet.getDataRange().getValues();
var headers = data.splice(0,1)[0]; // remove headers from data
data.sort(reverseTimestampOrder); // Sort 2d array
data.splice(0,0,headers); // replace headers
var destId = "--copy-sheet-ID--";
var destSheet = SpreadsheetApp.openById(destId).getSheetByName('Sheet1');
destSheet.clear();
destSheet.getRange(1,1,data.length,data[0].length).setValues(data);
};
function reverseTimestampOrder(a,b) {
// Timestamp is in first (zero-th) column
return (b[0]-a[0]);
}
If someone could also point me to a list of commands with basic syntax I'd be grateful.
The Google Apps Script API classes and methods reference is here. If you're learning, try the tutorials (same place), and I recommend you get familiar with Javascript through some form of e-learning - CodeAcademy.com is a good place to start, since it introduces all the language constructs without focusing on web page development, making it very relevant for Googls Apps Script.