Google Sheets Script - paste values based on time trigger - google-apps-script

I'm trying to do a few things with a piece of script but cannot get it all down.
In row 2 of my sheet I have a series of consecutive dates based on the first of each month (British notation) e.g 01/08/2016, 01/09/2016, 01/10/2016. I then have a formula in rows 14 and 15 which I would like to be fixed (copy / paste value) when today's date matches that in row 2.
I feel that I need to run a few things -
Schedule a script to run once per day to check if any value in row 2 is equal to today's date. If true then...
Copy / paste values of the numbers in rows 14 and 15 and the column where the date matches.
Maybe an index/match is needed to verify part 1 but I'm really in the dark on how to do it.
Thanks for any assistance.

I think that I've now managed to crack it. In row 1 I have put in a helper formula which checks the date in row 2 against today's date and puts "TRUEMONTH" in the cell if it matches. My clunky code is then as follows (it still needs some tidying up for the sheet refs etc) -
function ColCheck() {
var name = "TRUEMONTH";
var name1 = "PASTE_ME_AS_VALUE";
var range = SpreadsheetApp.getActiveSheet().getDataRange()
var values = range.getValues();
var range1 = SpreadsheetApp.getActiveSheet().getDataRange()
var values1 = range1.getValues();
for (var row in values) {
for (var col in values[row]) {
if (values[row][col] == name) {
for (var row1 in values1) {
for (var col1 in values1[row1]) {
if (values1[row1][col1] == name1) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var row = parseInt(row1) + 1;
var col = parseInt(col) + 1;
var source = ss.getActiveSheet().getRange([row],[col]);
source.copyTo(ss.getActiveSheet().getRange([row],[col]), {contentsOnly: true});
}
}
}
}
}
}
}

Related

Google app script for loop is extremely slow

I have a pivot table set up in a google sheet file where I've labeled the sheet pivot table 1. I want to get each value of the first column of the pivot, duplicate each values 12 times in an array and paste these values in the 3rd column of sheet 5. However, it seems extremely slow to do with the script never really completing (just takes 10+ minutes before I've cancelled it).
The pivot has approximately 3000 lines, which would result in a 3000 * 12 = 36000 array.
Any thoughts on how I can optimize this?
function test2() {
// activating current spreadsheet for use
var spreadsheet = SpreadsheetApp.getActive();
//empty array
var array_dept = []
// returns (integer #) the last row of the pivot table 1 sheet that has content
var last_row = spreadsheet.getSheetByName("Pivot Table 1").getLastRow();
// Get value in pivot table 1 from range of row 1 (dept name), column 1, all the way to last row
// Then paste it in sheet5 from row 1, column 3, all the way to the last row defined above
for (var i = 1; i < last_row; i++ )
{
//get value and then paste it in a destination
var value_dept = spreadsheet.getSheetByName("Pivot Table 1").getRange(i,1).getValue();
array_dept.fill(value_dept, -12 + (12*i) , 12*i)
}
destination_dept = spreadsheet.getSheetByName("Sheet5").getRange(1,3, last_row);
destination_dept.setValues(array_dept);
}
You don't need use a loop if you know the first row and the last row on the source column. You can just define the range:
var pivotRange = pivot.getRange(1,1,last_row)
var targetRange = target.getRange(1,3,last_row)
doc ref; this is just one of five methods to define a range.
In the OP script, there would be 3000xgetRange + 3000xgetValue. In this answer there are: 2xgetRange and 1 x getValue. This should account for a substantial amount of script processing. Of course, we know nothing of the rest of the spreadsheet (its size, formula, functions, triggers, etc). But all other things being equal, this should improve the performance of the script.
function test2() {
// activating current spreadsheet for use
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet()
var pivotSheetName = "Pivot Table 1"
var pivot = spreadsheet.getSheetByName(pivotSheetName)
//temporary array
var array_dept = []
// returns (integer #) the last row of the pivot table 1 sheet that has content
var last_row = pivot.getLastRow();
//Logger.log("DEBUG: last row in the pivot table:"+last_row)
var pivotRange = pivot.getRange(1,1,last_row)
// Logger.log("DEBUG: the range for the pivot range = "+pivotRange.getA1Notation())
var pivotData = pivotRange.getValues()
//Then paste it in sheet5 from row 1, column 3, all the way to the last row defined above
var targetSheetName = "Sheet5"
var target = spreadsheet.getSheetByName(targetSheetName)
var targetRange = target.getRange(1,3,last_row)
// Logger.log("DEBUG: the range for the target range = "+targetRange.getA1Notation())
targetRange.setValues(pivotData)
Logger.log("Done")
}

IMPORTRANGE excluding all rows that come after the first empty row

I'd like to know how can I can import a range excluding all rows from the source that come after the first empty row.
For example, let's say I have a sheet with 200 rows, but the row 101 is empty, but from 102 to 200 all rows have values. I'd like to import only rows 1:100.
I need to do it independently of how many rows has the source sheet, because we regularly import data and the number of valid rows grows on every import.
it would be like this:
=ARRAYFORMULA(
IMPORTRANGE("ID_or_URL", "Sheet3!F1:F"&MIN(IF(
IMPORTRANGE("ID_or_URL", "Sheet3!F1:F")="", ROW(A:A), ))))
Adding to what's already here, if you would like to do this more generically using Apps Script, you can bind this to the sheet and run as you need.
Assuming that Column A is being used for the data in the Sheet to Copy to, you can find the first row which is blank by running
function obtainFirstBlankRow() {
var sheet = SpreadsheetApp.getActive().getSheetByName('SHEET-TO-COPY-TO');
// search for first blank row
var col = sheet.getRange('A:A');
var vals = col.getValues();
var count = 0;
while (vals[count][0] != "") {
count++;
}
return count + 1;
}
This will return a number corresponding to the first row in which the cell in the A column is blank - you can change which column it checks if you need by replacing A:A in sheet.getRange() to the range you need.
You can then use:
function myFunction() {
var startRow = 1; //assuming you want to copy from row 1
var endRow = obtainFirstBlankRow();
var startCol = 1; // assuming you want to copy starting at column A
var endCol = 26; // assuming copying values up to column Z
var startRowPaste = 1; //assuming you want to copy starting at row 1
var startColPaste = 1; // assuming you want to copy starting at column A
//example range to copy from
var rangeToCopy = SpreadsheetApp.openById('IDOfSheetToCopyFrom').getSheetByName('SheetToCopyFrom')
var rangeToCopy = rangeToCopy.getRange(startRow, startCol, endRow, endCol);
//make sure the range you're copying to is the same size
var newRange = SpreadsheetApp.openById('IDOfSheetToCopyTo').getSheetByName('SheetToCopyTo')
var newRange = newRange.getRange(startRowPaste, startColPaste, rangeToCopy.getNumRows(), rangeToCopy.getNumColumns());
newRange.setValues(rangeToCopy.getValues());
}
To copy the data to the new Range.

Output of GAS function not the individual cell value, but the full column of values

I'm new to Google Apps script. I created a Google Apps Script to act as a alternative for vlookup (I want static data for reasons).
I've created a function that is able to communicate between Sheet 1 "Main Sheet" (user input), and Sheet 2 "Query Sheet" (contains data results).
Intention is to compare mainSheet ColW with querySheet ColA
If mainSheet ColW matches, replace the value in mainSheet ColX with the value of querySheet ColB in the corresponding row.
Example if mainSheet Col W2 is '123' and querySheet Col A5 is '123', paste value from querySheet Col B5 into mainSheet Col X2
This is for comparing input provided by a user with data that is updated in the backend after X period of time. To reduce the need to constantly review this data, the query runs in intervals. The user sheet is populated with results.
function match(){
//Sheet with the query outputs
var querySheet = SpreadsheetApp.openById("SHEETID123456789"); // query results are dumped in this doc
var querySheetTab = querySheet.getRange("query!A1:F"); //tab with the results
//Main sheet to have query data pasted
var mainSheet = SpreadsheetApp.getActiveSpreadsheet(); //current worksheet
var mainSheetTab = mainSheet.getRange("USERSHEET!W2:X"); //Tab and range of the data. W contains a number. X is where the results of B from the querySheet go
//values to compare
var mainSheetVal = mainSheet.getSheetByName('USERSHEET').getRange('W2:W').getValues(); //values to match against query results
var mainSheetComp = mainSheet.getSheetByName('USERSHEET').getRange('W2:W'); //numbers to match against query results
var mainSheetSet = mainSheet.getSheetByName('USERSHEET').getRange('X2:X'); //Where values get pasted.
var getValQS = querySheet.getSheetByName('query').getRange('A1:B').getValues(); //query results includes both objects values
var valQS = querySheet.getSheetByName('query').getRange('A1:A'); // query results
var valSet = querySheet.getSheetByName('query').getRange('B2:B'); //desired values to paste.
var output = [];
for (var i = 0; i < mainSheetVal.length; i++) {
mainSheetSet.setValue(getValQS[i][0]);
SpreadsheetApp.flush();
Utilities.sleep(10000);
if (mainSheetComp.getValue() == '') {
output.push([getValQS[i][0]]);
}
}
mainSheet.getRange(2, 12, output.length, 2).setValues(output);
}
This sheet ends up replacing X2:X with the value of A1, then A2, and continues to cycle through even with empty cells. I haven't been able to set-up a proper compare.
You want to compare the value of column "W" of USERSHEET sheet with the value of column "A" of query sheet.
When above both values are the same, you want to put the value of column "B" of query sheet to the cell of column "X" of USERSHEET sheet.
If my understanding is correct, how about this modification? Please think of this as just one of several answers.
Modification points:
Values of USERSHEET were retrieved from "W:X" by one call.
Values of USERSHEET were modified when the value of column "A" of query is the same with that of column "W" of USERSHEET.
After the values of USERSHEET were modified, the modified values were put to USERSHEET.
Modified script:
function match(){
var querySheet = SpreadsheetApp.openById("SHEETID123456789").getSheetByName("query");
var queryValues = querySheet.getRange(1, 1, querySheet.getLastRow(), 2).getValues(); // A:B
var mainSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("USERSHEET");
var mainRange = mainSheet.getRange(2, 23, mainSheet.getLastRow() - 1, 2); // W:X
var mainValues = mainRange.getValues();
for (var i = 0; i < mainValues.length; i++) {
for (var j = 0; j < queryValues.length; j++) {
if (mainValues[i][0] == queryValues[j][0]) {
mainValues[i][1] = queryValues[j][1];
}
}
}
mainRange.setValues(mainValues);
}
Note:
In this modification, the column "W" and "X" of USERSHEET is overwritten. If you want to changed this situation, please tell me.
If I misunderstood your question, I apologize.
Try this:
This compares Column W of main sheet to Column A of query sheet and puts value in column B of the query sheet into column X in main sheet in the same row as the match in column A. It could be a little faster by moving the setValue out of the loop but I didn't want to mess around over writing your other data. Also I have not tested this so there is always the possibility of needing corrections.
function match() {
var qss=SpreadsheetApp.openById('id');
var qsh=qss.getSheetByName('query');
var qrgA=qsh.getRange(2,1,qsh.getLastRow(),1);//colA
var qrgB=qsh.getRange(2,2,qsh.getLastRow(),1);//colB
var qvB=qrgB.getValues();
var qvA=qrgA.getValues();
var qvAf=qvA.map(function(r){return r[0]});//flatten array
var mss=SpreadsheetApp.getActive();
var msh=mss.getSheetByName('USERSHEET');
var mrg=msh.getRange(2,23,msh.getLastRow(),1);//colW
var mVa=mrg.getValues();
for(var i=0;i<mVa.length;i++) {
var index=qvAf.indexOf(mvA[i][0]);
if(index>-1) {
msh.getRange(i + 2,24).setValue(qvB[index][0]);//put matches in same row of columnX
}
}
}

Google Apps Script: Get specific data and preview elsewhere

I have a sheet of a long (sorted) list, with column A with date and column B with time and then some data on columns c,d,e,f.
I want to have a script function that compares current date-time to the date and time from columns A & B and then copies to columns i,j,k,l,m,n on rows 1-4 the "upcoming" data.
So I'm thinking of something like this, but unsure how to finish it. Any ideas?
function myFunction()
{
var d = new Date();
var now = d.getTime();
var as = SpreadsheetApp.getActiveSheet();
var data = as.getDataRange().getValues(); //this seems to be wrong, because I don't need all DataRange, only first 6 columns, but as many rows as those having data
var c = 0;
for(i in data)
{
var row = data[i];
//var rowdate = ??;
if (now < rowdate)
{
c++;
//copy from row to destination location
}
if (c > 4) break;
}
}

Automatically move data from one sheet to another in google docs

i have a spreadsheet that i keep track of tasks i need to do, once complete i enter a date in the last column. What i want is for that completed task to be moved to sheet 2.
At present i have sheet 1 named SUD_schedule and i want the completed row of data to be moved to sheet 2 named SUD_archive. I've looked through the forum posts already and i've tried a variation of scripts but so far no luck. The closest i have come is this script:
function onEdit() {
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();//Original sheet
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];//target sheet
// to act on only one sheet, check the sheet name here:
//If it si not first sheet, it will do nothing
if (sheet1.getSheetName() != "SUD_schedule") {
return;
}
//Get Row and column index of active cell.
var rowIndex = sheet1.getActiveRange().getRowIndex();
var colIndex = sheet1.getActiveRange().getColumnIndex();
//If the selected column is 10th and it is not a header row
if (colIndex == 16 && rowIndex > 1) {
//Get the data from the current row
var data = sheet1.getRange(rowIndex,1,1,9).getValues();
var lastRow2;
(sheet2.getLastRow()==0)?lastRow2=1:lastRow2=sheet2.getLastRow()+1;
//Copy the data to the lastRow+1th row in target sheet
sheet2.getRange(lastRow2,1,1,data[0].length).setValues(data);
}
}
Column P (16) is the task complete date, row 1 is frozen and contains column headers.
Can anybody help show where i'm going wrong.
Kind regards
Den
Your code is not generic and you are more complicating your objective. Below will work out your need.
function onEdit(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('SUD_schedule');
var sheet2 = ss.getSheetByName('SUD_archive');
var dateColumn = "16";
var array = []
var range = sheet1.getRange(1, 1, sheet1.getLastRow(), dateColumn);
for (var i = 2; i <= sheet1.getLastRow(); i++) //i iterates from 2 as you say R1 is header
{
if(isValidDate(range.getCell(i, dateColumn).getValue()) == true) //checking if any values on column16 is valid date
{
data = sheet1.getRange(i, 1, 1, dateColumn).getValues(); //Getting the range values of particular row where C16 is date
for (var j = 0; j < dateColumn; j++) //Adding the row in array
{
array.push(data[0][j]);
}
}
if(array.length > 0)
{
sheet2.appendRow(array); //Appending the row in sheet2
array = [];
sheet1.deleteRow(i); //deleting the row in sheet as you said you want to move, if you copy remove this and next line
i=i-1; //managing i value after deleting a row.
}
}
}
//Below function return true if the given String is date, else false
function isValidDate(d) {
if ( Object.prototype.toString.call(d) !== "[object Date]" )
return false;
return !isNaN(d.getTime());
}
I am not sure that the syntax you have as used below is entirely correct.
(sheet2.getLastRow()==0)?lastRow2=1:lastRow2=sheet2.getLastRow()+1;
sheet2.getRange(lastRow2,1,1,data[0].length).setValues(data);
What I know will work for certain is if you omit the variable lastRow2 all together and use this instead.
sheet2.getRange(getLastRow+1,1,1,data[0].length).setValues(data);
To complement Joachin's answer, here is how you can adapt that code if you don't have the date in the last row. In the below shown part of the code replace Lastcolumnumber with your last column.
//Getting the range values of particular row where C16 is date
data = sheet1.getRange(i, 1, 1, LASTCOLUMNNUMBER).getValues();
//Adding the row in array
for (var j = 0; j < LASTCOLUMNNUMBER; j++)