Issue with Google Script Clashing With QUERY Function - google-sheets

I have a sheet with is collecting form responses. I'm then using the QUERY function to pull all the data into a separate sheet to action using the following formula:
=QUERY(formsubmissions,"SELECT *")
where formsubmissions in the Named Range.
Once four steps have taken place the user then selects Completed = Ýes' which triggers the following script:
function onEdit() {
var sheetNameToWatch = "R+R;
var columnNumberToWatch = 15;
var valueToWatch = "Yes";
var sheetNameToMoveTheRowTo = "Completed";
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveCell();
if (sheet.getName() == sheetNameToWatch && range.getColumn() == columnNumberToWatch && range.getValue() == valueToWatch) {
var targetSheet = ss.getSheetByName(sheetNameToMoveTheRowTo);
var targetRange = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
sheet.getRange(range.getRow(), 1, 1, sheet.getLastColumn()).moveTo(targetRange);
sheet.deleteRow(range.getRow());
}
}
This script executes and functions correctly, moving the row into the Çompleted sheet and deleting from the active sheet. However, the row is only momentarily deleted and appears back almost instantaneously. I guess this is because it still matches the QUERY parameters so it is pulled in again straight away.
How can I ensure it stays deleted either by modifying the query or by modifying the script?

As you've surmised, this occurs because the row in the original responses sheet still fulfils the query criteria. You could modify your query to exclude any unique identifiers by checking that it doesn't already appear in the Completed sheet - Say that column A is a reference number or timestamp, you could try the following:
=QUERY(formsubmissions,"SELECT * where A <> "&Completed!A:A)
This then checks the array in Completed A:A for the same value, and excludes it from the query result if present.

Related

Can I dynamically add rows when a column is full?

Is it possible for my Google sheet to automatically append a new formatted row to the end of a column when it fills up?
I would like to avoid having to manually create, say, 1000 pre-formatted empty rows in my sheet.
This is possible using Google Apps Script onEdit() trigger.
Try:
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var range = sheet.getRange(1, 1, sheet.getLastRow(), 3); //Range is set from COL A - COL C up to last row with data
var lastRow = sheet.getLastRow();
var val = range.getValues();
var lastVal = val[val.length - 1];
//Check if the edit happened within Columns A-C
var range = e.range
var col = range.getColumn();
//Set sheetname and cell range where the options are coming from
var options = ss.getSheetByName("Names").getRange("A2:A")
var dropdownCell1 = sheet.getRange(lastRow + 1, 1);
var dropdownCell2 = sheet.getRange(lastRow + 1, 2);
if (col>= 1 && col <= 3 && !lastVal.includes('')) { //checks if the last row of data is filled
//Sets the data validation rule to the cells
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(options)
dropdownCell1.setDataValidation(rule)
dropdownCell2.setDataValidation(rule)
}
}
Result:
Explanation:
This uses onEdit() trigger which runs every time you make edit to a cell value. This then checks if the edit happens within Columns A-C and checks if the last row from Columns A-C are filled. It then adds the data validation rule to the cells. The dropdown comes from a sheet called 'Names' and the options are listed there.
I just did not add a code to reverse it if a data is deleted. You may edit the range if you are using different columns. Hope this helps!
Format the sheet down to the last row, as tested here in this GIF using Sequence(20), Sequence(2000).

In Google Sheets, I want a sheet's name to be updated based value of a cell (which changes regularly based on a formula)

In Google Spreadsheet, I want the name of the sheet to be updated and be whatever the value is in a cell. The value of the cell changes based off a formula. Is it possible?
Try
function onEdit(event){
var sh = event.source.getActiveSheet();
var cel = event.source.getActiveRange();
if (cel.getA1Notation()=='X1' || cel.getA1Notation()=='Y1'){
sh.setName(sh.getRange('Z1').getValue())
}
}
where
X1, Y1 ... are the deeper origin of the data, manually updated
Z1 the cell that contains the expected name of the sheet
If the sheet you want to rename is not the active one, you have to identify it by it's ID, you have first to test event.source.getActiveSheet() and then determine the sheet to be renamed, for instance:
var shId = 1353792204
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheets().filter(function(s) {return s.getSheetId() === shId;})[0];

Problem with recording data change in google sheet

I have two sheets. On one sheet (Sheet1), I have a column 'A' which has 5 fixed values, and a column 'B' which records the timestamp value in 'A' change. The other sheet (Sheet2) is meant to record all the changes in the first sheet in one day.
I use this simple query in the recording sheet:
=QUERY(Sheet1!A$1:X,"select * where C>="& Sheet2!D1)
with Sheet1!C has timestamps and Sheet2!D1 is the timestamp of 12:00 AM today
The problem is when I change the value of a row in C columns more than one time, instead of creating a new row in Sheet2 it change the value of that row in Sheet2 into new values.
So how do I change my code to get my desire results?
EDIT 2: here is my new code, but it doesn't help.
function importdata(x) {
// var row = x.range.getRow();
// var col = x.range.getColumn();
var addedsheet=SpreadsheetApp.getActive().getSheetByName("Change"); // Sheet where I want to keep the record of the change
var original_sheet = SpreadsheetApp.getActive().getSheetByName("Master"); //sheet where the change is happended
var compared_value = addedsheet.getRange(1,4).getValue(); // Cell D1 of sheet "Change", which has timestamp of today
var insert_area = original_sheet.getRange("A2:X").getValues() // area to get value from "Master" sheet to put into "Change"
var compared_area = original_sheet.getRange("C2:C").getValues(); // area where has timestamp
if (compared_area >= compared_value){
addedsheet.values.append([insert_area])}
} //if timestamp of one row from Master is greater than the value at Change!D1 =>append that row at the end (this is what I'm trying to do)
EDIT 3: I fixed the above code by append[insert_area][0] instead of [insert_area]
But then I have a new problem: there will a chance that a row in sheet 1 will be overwrited in sheet 2. I try something like this, but it returns nothing on the sheet.
function for_each_row(){
var addedsheet=SpreadsheetApp.getActive().getSheetByName("Change"); // Sheet where I want to keep the record of the change
var original_sheet = SpreadsheetApp.getActive().getSheetByName("Master"); //sheet where the change is happended
var compared_value = addedsheet.getRange(1,4).getValue(); // Cell D1 of sheet "Change", which has timestamp of today
var number_of_row_2 = addedsheet.getLastRow;
var number_of_row_1 = original_sheet.getLastRow();
for (var i=2; i<number_of_row_1 +1; i++){
var compared_stamp = original_sheet.getRange("C"+i).getValues();
var insert_values = (original_sheet.getRange(i,1,1,24).getValues())
if (compared_stamp > compared_value){
var insert_values = (original_sheet.getRange(i,1,1,24).getValues());
for (var j = 2; j<number_of_row_2 +1; j++){
var value_from_sheet = addedsheet.getRange(j,1,1,24).getValues();
if (insert_values ===value_from_sheet){
return
}
else(
addedsheet.appendRow(insert_values[0]))
}
}
}
}
My thought is if a row satisfies the 1st condition then the value will be check in sheet 2. If sheet 2 didn't have that row then append that row.
Issue:
If I understand you correctly, you want to do the following:
If sheet Master is edited, iterate through all rows in this sheet (excluding headers), and for each row, check the following:
Column C has a higher value than cell D1 in sheet Change.
This row, with these exact values, does not exist in Change.
If these conditions are meet, append the row to Change.
Solution:
Use filter and some to filter out rows that don't match your two conditions, and use setValues to write the resulting rows to your other sheet.
Code snippet:
function onEdit(e) {
var editedSheet = e ? e.range.getSheet() : SpreadsheetApp.getActiveSheet();
if (editedSheet.getName() === "Master") {
var addedSheet = SpreadsheetApp.getActive().getSheetByName("Change");
var compared_value = addedSheet.getRange(1,4).getValue();
var newData = editedSheet.getRange("A2:X" + editedSheet.getLastRow()).getValues();
var currentData = addedSheet.getRange("A2:X" + addedSheet.getLastRow()).getValues();
var filteredData = newData.filter(row => row[2] >= compared_value)
.filter(row => !currentData.some(currentRow => JSON.stringify(currentRow) === JSON.stringify(row)));
addedSheet.getRange(addedSheet.getLastRow()+1,1,filteredData.length,filteredData[0].length).setValues(filteredData);
}
}
Note:
An easy way to check if two rows have the same values is using JSON.stringify(row).
I'm assuming the timestamps are not Dates. If they are, you should compare them using getTime(). So you should change the corresponding code to newData.filter(row => row[2].getTime() >= compared_value.getTime()).

Script to copy data from one google sheet to another based on a condition

I have a google spreadsheet called the master list. It contains two sheets 'Response' and 'Master Response'. The response sheet has around 3000 rows of data and the first column in the 'response' sheet is called ' responded?'. I want to copy the row from 'response' sheet to ' master response' sheet if the 'responded?' column has the value 'called' in it and every time a new entry is made in the 'response' sheet the appropriate row of data should be copied to the 'master response' sheet. I am currently using the following code but the 'master response' is blank. I am new to using the google script editor and am not sure if the code is wrong or execution is not completed properly. Any help would be appreciated. Thank you
function onEdit()
{
var sheetNameToWatch = "Response";
var columnNumberToWatch = 1;
var valueToWatch = "Called";
var sheetNameToMoveTheRowTo = "Master Response";
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var range = sheet.getActiveCell();
if (sheet.getName() == sheetNameToWatch && range.getColumn() ==
columnNumberToWatch && range.getValue() == valueToWatch) {
var targetSheet = ss.getSheetByName(sheetNameToMoveTheRowTo);
var targetRange = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
sheet.getRange(2, 1, sheet.getLastRow(),
sheet.getLastColumn()).moveTo(targetRange);
}
}

Google script to send conditional emails based on cells in google sheets

I have a google sheet that I would like to have generate an email alert when one column is greater than the other. Specifically, column F > column G. Here is what I have so far, any advice would be greatly appreciated, as I do not have much skill writing functions.
function readCell() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Watch list");
var value = sheet.getRange("F2").getValue();
var value1 = sheet.getRange("G2").getValue();
if(value>value1) MailApp.sendEmail('example#gmail.com', 'subject', 'message');
};
Currently this only attempts to compare cell F2 to cell G2. Is there a way to make the function compare the entire F column against column G, and generate an email for each individual case where Fx > Gx ?
Thank you!!
You have to loop all over the range.
first instead of getting the content of one cell you'll need to get the content of all the column:
var value = sheet.getRange("F2").getValue();
become that
var values = sheet.getRange("F2:F").getValues();
(same for value1)
then you need to create an empty table that will collect the results:
var results = [];
and now you need to loop throught all the values:
for(var i=0;i<values.length;i++){
//do the comparaison and store result if greater for example
}
then you may send the result.
all put together it give something like that:
function readCell() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Watch list");
var values = sheet.getRange("F2:F").getValues();
var value1s = sheet.getRange("G2:G").getValues();
var results = [];
for(var i=0;i<values.length;i++){
if(values[i]<value1s[i]){
results.push("alert on line: "+(i+2)); // +2 because the loop start at zero and first line is the second one (F2)
}
}
MailApp.sendEmail('example#gmail.com', 'subject', results.join("\n"));
};
If you want to trigger that function automatically you'll also need to change the way you call the spreadsheet (instead of getActive.... you'll need to use openById)

Resources