I have a Google Form that is connected to a Google Spreadsheet. I noticed that if I decide to manually write something on this Google Spreadsheet, the order of entry is not preserved during future responses.
For example: Assume there are 3 rows each with a Google Form response and I manually write something in the 4th row. The next time there is a Google Form response, the spreadsheet will show the new response in the 4th row, and move my manual input to the 5th row.
I was wondering if there was anyway to get the new responses to show up below any manual inputs so that the order is preserved. (Have 3 rows with Google form data, the 4th row with the manual input, and 5th row with the Google Form response.)
You can create an onFormSubmit Trigger that will check if there is a manual input in the spreadsheet by comparing the last row and the response row. If the response row is not equal to the last row, it means a manual input has been made and we move the response to the last row.
Go to Tools -> Script editor and paste the code below:
function moveToLastRow(e) {
var sheet = e.range.getSheet();
var lastRow = sheet.getLastRow();
var row = e.range.getRow();
var data = e.values;
if(lastRow != row){
sheet.deleteRow(row);
sheet.appendRow(data);
}
}
Create an onFormSubmit Trigger:
Open your Apps Script project.
At the left, click Triggers alarm.
At the bottom right, click Add Trigger.
Select and configure the type of trigger you want to create.
Click Save.
For this case, copy these configurations:
Output:
References
Event Object
Installable Trigger
Class Sheet
Related
I have two spreadsheets, one client facing and one internal facing, that use IMPORTRANGE and QUERY + IMPORTRANGE to display data between one another.
The internal sheet is used to write copies for social media posts, quality check them and give approval. Once approval is given the client sheet needs to update to show the copies on the identically named pages AND in the correct rows.
Approval is given by ticking a checkbox in the internal sheet and the QUERY + IMPORTRANGE formula then pulls that into the client sheet. However, my abilities stop where I have to display this in the correct row position no matter which of the tickboxes are checked.
=iferror(QUERY(IMPORTRANGE("https://docs.google.com/spreadsheets/d/1CIXhcuTigYbggmPjBKpvnH7P-cjBDQfSIcmKN6_0T5M/edit#gid=2018991957","Cloud in FS Survey!C2:N"),"SELECT Col1, Col2, Col3, Col4, Col5, Col6 WHERE Col11 = TRUE"),"No copies approved")
I've seen some discussions of having a second sheet for of the categories (there are 8 in the non-redacted document) and using VLOOKUP to match an ID to the imported row of data, hence I've created 'Row' columns in all tabs on both sheets with the unique row number. However, I want to avoid having hidden tabs in the client facing sheet where they can be revealed by the client.
Any assistance would be appreciated.
Many thanks,
Mark
There are 2 ways to deal with it. If you a add serial number in sheet 1 where approvals are given, you may add another element to your query by adding order by serial no. column ASC
Else you could do the same with order by date column ASC
This way new additions will add below the imported data because they will always be in chronology and not mess up the order of older data.
Having direct importranges where static data exists is always risky.
Alternately, you could also use ID thing without creating an additional hidden sheet, direct vlookups with importrange nested inside the vlookup.
For example, =arrayformula(vlookup(search key, importrangexxx, index, false)
The best solution would be to have a hidden sheet. But all of the above can be decent alternate if you're not dealing with thousands of rows.
If you don't want to use helper sheet or column, you can use Apps Script and onEdit Trigger.
Using onEdit Trigger, you can run a function automatically when a user click the checkbox. Inside that function is a list of commands that will write data to the client side.
Example:
To start with Apps Script:
Go to Tools -> Script Editor.
In your script editor, delete the code in Code.gs
Paste the code provided below.
Click Save.
Code:
function showToClientSide(e) {
var val = e.value;
var range = e.range;
var row = range.getRow();
var col = range.getColumn();
var sheet = range.getSheet();
var clientSS = SpreadsheetApp.openById("Insert Client Sheet ID here")
if(val == "TRUE" && row > 1 && col == 13){ // check if checkbox is checked
var data = sheet.getRange(row, 3, 1, 9).getValues(); //get data from internal
var sh = clientSS.getSheetByName("Cloud in FS Survey"); //client sheet
sh.getRange(row, 3, 1, 9).setValues(data); //write internal data to client
}else if(val == "FALSE" && row > 1 && col == 13){
var sh = clientSS.getSheetByName("Cloud in FS Survey"); //client
sh.getRange(row, 3, 1, 9).clearContent(); //delete data when unchecked
}
}
To setup your Installable Trigger (onEdit):
In the left menu of your Apps Script, click Triggers
Click Add Trigger
Copy the setup below.
Save and Authorize the script.
Testing:
Internal Sheet:
Client Sheet:
Checkbox checked:
Internal:
Client:
Checkbox unchecked:
Internal:
Client:
References:
Installable Triggers
Event Objects
Class Range
Class Sheet
Hello i'm using google sheet as my second databse and in the main page called Companies i have a list of companies as shown below:
and i wrote a function that generate a new tab for every companie in the first column. here is what a tab looks like
my goal is in the companies tab under "Workers" i want to get the value of "Total workers" of each companie. the list of companies will be constantly growing so i thought about maybe a function that uses the value of the first column to search for the tab and then get the value of G2.
I am really new to google sheet and i would appreciate any help on how to solve this problem
SUGGESTION
You can try this sample script below with custom function named getTotalWorkers & then add it as a bound script to your Spreadsheet file:
UPDATED Script:
function getTotalWorkers(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var names = ss.getRange("Company!A2:A").getValues().filter(String); //get names of the sheets on column A
var res = [];
for(x=0; x<names.length; x++){
var data = ss.getRange(names[x]+"!G2").getValue(); //get the current cell G2 values on every sheet tabs
res.push([data]); //place all values to a tem[orayr array variable
}
ss.getSheetByName("Company").getRange(2,6,res.length,1).setValues(res); //add the values under the "Workers" column on Company sheet tab
}
Sample Demonstration
After saving the script from the Apps Script editor, place the updated getTotalWorkers function to a time-driven trigger:
The time driven trigger will auto populate the "Workers" F column cells every minute (based on my sample time-driven trigger configuration):
Wants to achieve the same feature with Google Sheets as explained here with MS Excel (https://www.youtube.com/watch?v=5GLjQNeiEeU)
Only use column A
1st Row = Header (Cell A:1)
2nd Row (Cell A:2) should always be the cell that one enters data into.
When you type data and press Enter, the data should move down to Cell A:3 and Cell A:2 should be
empty again in order to accept the next data input, etc.
Can anyone please assist with a basic script?
Many Thanks
UJ Library
You need to create Apps Script that will handle an event such as editing sheets and use the Event Object to determine which cell is being edited.
Events can be catch using Triggers. Triggers let Apps Script run a function automatically when a certain event, like editing a sheet, occurs.
Event Object contains information about the context that caused the trigger to fire.
Example:
Code:
function onEdit(e){
var range = e.range;
if(range.getA1Notation() == "A2"){
//Move A2 value below
range.insertCells(SpreadsheetApp.Dimension.ROWS);
//set A2 as active cell
range.activate();
}
}
Before:
After pressing Enter:
References:
Simple Triggers
Event Object
range.activate()
range.insertCells(shiftDimension)
I am using Zapier to insert external content into a Google Spreadsheet tab automatically and it appears to insert a row directly under the last non-empty row rather than replacing the content in the next empty row.
So if row 7 was the last row, Zapier inserts a new row under 7 and the old row 8 becomes row 9.
I then have two other tabs, both viewable on the web (where the first isn't) that reference the content in the first tab but as new content is added, these references in the format of data!a8 also gets moved. If I use data!$a$8 then I have to change 500 rows manually.
With or without coding, is there a way to allow Zapier to insert the rows but still reference that row automatically?
For those attempting to do this without script, it appears it is not possible.
Script
Create a function like the one below. Note we are reading one sheet (AutomaticImport) and writing to the sheet WebData
function fncUpdateWebData() {
var ss = SpreadsheetApp.getActiveSpreadsheet(),
sheet = ss.getSheetByName("AutomaticImport"),
sheetWeb = ss.getSheetByName("WebData"),
arrRides = sheet.getDataRange().getValues(),
row,col,iOutputRow,strURL;
iOutputRow = 2;
for ( iRow = 1; iRow < arrRides.length; iRow++) {
sheetWeb.getRange(iOutputRow,1).setValue(arrRides[iRow][5]); // date
strURL = '=HYPERLINK("' + 'https://www.strava.com/activities/' + arrRides[iRow][7] + '","' + arrRides[iRow][8] + '")';
sheetWeb.getRange(iOutputRow,2).setValue(strURL); // start & link
sheetWeb.getRange(iOutputRow,3).setValue(arrRides[iRow][4]); // Distance
sheetWeb.getRange(iOutputRow,4).setValue(arrRides[iRow][0]); // Avg Speed
iOutputRow++;
}
}
In Resources/Current Project Triggers and a new trigger and use your new function name, "from Spreadsheet" and select "onChange"
I hope his helps get you over the hurdle.
I know this is a really, really late answer, but I was having the same issue and almost used the accepted solution before realizing there's a much better and easier way.
It's actually in the Zapier docs, right here. The solution is to create two Google Sheets, one for Zapier and the other for your formulas. Hook Zapier up to the first sheet, and then use the IMPORTRANGE command to copy the necessary columns into the second sheet. Write your formulas in the second sheet, and you'll notice that the extra layer of indirection stops Google from rewriting them when a row is added to the source range!
Tl;dr: RTFM, as usual.
I saw several complains about the delay of updating data through IMPORTRANGE in Google Sheets but I need the opposite and don't want the second sheet to get updated automatically, just update at the end of the day for example.
The code is already like this:
=IMPORTRANGE("The Key","The_Page!B:D")
I hacked around this by, on both spreadsheets by creating a refresh loop using a NOW cell, with both spreadsheets crossreferencing each other's NOW cell.
When the original sheet gets appended with a form submission or something, it updates its own NOW cell, and reupdates own IMPORTRANGE cell. The second spreadsheet follows suit, updating its own NOW cell to provide the original sheet with the correct data. Because the second spreadsheet has updated itself, it also updates the main IMPORTRANGE which refreshes the data you want it to display in the first place, as well as the IMPORTRANGE cell which gets the NOW cell from the original spreadsheet
At least, I'm pretty sure that's how it works. All I know, and all I care about, frankly, is that it works
Maybe you need to use the script editor and write a simple function of the kind:
function importData()
{
var ss = SpreadsheetApp.getActiveSpreadsheet(); //source ss
var sheet = ss.getSheetByName("The_Page"); //opens the sheet with your source data
var values = sheet.getRange("B:D").getValues(); //gets needed values
var ts = SpreadsheetApp.openById("The Key"); //target ss - paste your key
ts.getSheetByName("Name of the target sheet").getRange("B:D").setValues(values);
}
And then add a time-driven trigger to this project (Resources > Current project's triggers > Add a new one).