How to Setvalues off of an array google GAS for spreadsheets - javascript

I have been using google scripts for a week, and I have searched as much as I could to get the answer. Can someone please help me? I wrote a simple script to evaluate if a course is online based on the last three digits of a course number(i.e PSY-250-400). The script works fine, and I pushed the result into the end of the array. I don't know how to write back to google sheets. Below is what I have. Currently it will set the values based on the first result(online course). So all values are set to online. I am running it on 7 rows right now, but will need to run it on 20,000.
function onlineonly(online){
var sheet = SpreadsheetApp.getActiveSheet();
var students = sheet.getRange('A2:D7').getValues();
var online = ["400","401","403","404","600"];
var m;
var section;
for(var i=0; i<students.length; ++i){
section = students[i][3].substring(8,13);
for(var j = 0;j<online.length; j++){
if(section===online[j]){
section = m;
}
}
if(section === m){
students[i].push("online");
} else {
students[i].push("not online");
}
var method = [];
for(var k = 0; k<students.length; k++){
if(students[i][4]=== "online"){
method = "online";
} else {
method = "in person";
}
sheet.getRange('c2:c7').setValue(method);
}
}
}

The important thing to remember is that the dimensions of the Range must equal the exact dimensions of the Array[][]. This array must be two-dimensional! Otherwise you'll get an error that setValues() method expects an Object[][], not an Array.
You're trying to set a simple array. Also, the method you'll use is setValues(), not setValue().
Your code is a little hard to understand, so this is an example of the pattern:
function writeOutValues() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var range = sheet.getRange("C2:C7");
var values = range.getValues();
//remember, a 2d array is a grid of values that corresponds to the grid of the range like so: values[row][column]
//so in this range, values[0][0] = C2, values[1][0] = C3, etc.
values[0][0] = "New Value";
values[1][0] = "Another one";
//to set value,
range.setValues(values);
}

just move inner for loop
for(var k = 0; k<students.length; k++) to outside the main for loop
and apply the technique as told by zbnrg
here is working code
function onlineonly()
{
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var students = sheet.getRange('A2:C4').getValues();
var online = ["400","401","403","404","600"];
var m;
var section;
for(var i=0; i<students.length; ++i)
{
section = students[i][1].slice(8,11);
for(var j = 0;j<online.length; j++)
{
if(section===online[j])
{
section = m;
}
}
if(section === m)
{
students[i].push("online");
}
else
{
students[i].push("not online");
}
}
var range = sheet.getActiveSheet().getRange("C2:C4");
var method = range.getValues();
for(var k = 0; k<students.length; k++)
method[k][0] = students[k][3]==="online"?"online":"in person";
Logger.log(method[0][0] +" "+method[1][0] +" "+ method[2][0])
range.setValues(method);
}
here is my spreadsheet https://docs.google.com/spreadsheets/d/1iA9v_3rPH9JAhAmt2EJTdbqTQkFEl7yewawPy3w4YIg/edit#gid=0

Related

Changing existing script for Finding Duplicates in Google Sheets and entering "yes" in the last column instead of coloring it

I am new here and have no idea what I am doing. I found a script online and thought it would work for what I needed, but I would like to change it some. Any help would be appreciated. I have already changed it a little. If it would help to share the form with someone I can do that since it is only in the testing stages.
What I am wanting to do is instead of coloring the duplicates red in the main sheet I want it to put "Yes" in the last column of the main sheet. I would also like it to ignore the blank rows. Is this possible? (I copied the script I am currently using below)
function findDuplicates() {
// List the columns you want to check by number (A = 1)
var CHECK_COLUMNS = [13];
// Get the active sheet and info about it
var sourceSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("USE THIS SHEET FOR DATA").activate();
var numRows = sourceSheet.getLastRow();
var numCols = sourceSheet.getLastColumn();
// Create the temporary working sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var newSheet = ss.insertSheet("FindDupes");
// Copy the desired rows to the FindDupes sheet
for (var i = 0; i < CHECK_COLUMNS.length; i++) {
var sourceRange = sourceSheet.getRange(1,CHECK_COLUMNS[i],numRows);
var nextCol = newSheet.getLastColumn() + 1;
sourceRange.copyTo(newSheet.getRange(1,nextCol,numRows));
}
// Find duplicates in the FindDupes sheet and color them in the main sheet
var dupes = false;
var data = newSheet.getDataRange().getValues();
for (i = 1; i < data.length - 1; i++) {
for (j = i+1; j < data.length; j++) {
if (data[i].join() == data[j].join()) {
dupes = true;
sourceSheet.getRange(i+1,1,1,numCols).setFontColor("red");
sourceSheet.getRange(j+1,1,1,numCols).setFontColor("red");
}
}
}
// Remove the FindDupes temporary sheet
ss.deleteSheet(newSheet);
// Alert the user with the results
if (dupes) {
Browser.msgBox("Possible duplicate(s) found and colored red.");
} else {
Browser.msgBox("No duplicates found.");
}
};

Why does this for loop not work in Google scripts

i wrote the following loop based on the code found here:
How do I iterate through table rows and cells in javascript?
function myRowLooper() {
var inputSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('INPUT');
var inputRange = inputSheet.getRange(2,inputSheet.getLastColumn(),inputSheet.getLastRow());
for (var i = 0, row; row = inputRange.rows[i]; i++) {
Logger.log(row);
for (var j = 0, col; col = row.cells[j]; j++) {
Logger.log(col);
}
}
}
but when I apply it to Google scripts it throws an error: "TypeError: Cannot read property "0" from undefined."
What's causing this?
Because you can't get any value from 'inputRange.rows[i]'.
You may do something like this :
function myRowLooper() {
var inputSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
for(var i = 1; i<=inputSheet.getLastRow(); i++){
for(var j = 1; j<=inputSheet.getLastColumn(); j++){
var cell = inputSheet.getRange(i,j).getValue();
Logger.log(cell);
}
}
}
Hope this will help you.
Thanks.
Your code is extremely sloppy. You are trying to combine variables and condense unnecessarily and it's leading to errors in your code. There's no use in added "efficiency" if it leads to errors and mistakes.
Try something like this --
function yourFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Name");
var lastRow = sheet.getLastRow();
var lastColumn = sheet.getMaxColumns();
var conditionToCheckColumn = (lastColumn - 1);
var conditionRange = sheet.getRange(2, checkColumn, (lastRow - 1), 1);
var check = checkRange.getValues();
for (var i = 0; i < check.length; i++) {
if (check[i] == condition) {
continue;
} else {
//your code here
}
}
}
This will pull a range at which you can check it's value/ condition and if matches, it does nothing. If it does not match, it will perform code. It will then loop to the next row until the last row in your check range that has data.
Be warned - functions count as data despite the cell being visibly empty. If your sheet uses functions like =QUERY, you will have an infinitely looping code unless your =QUERY (or other fx()) has a specific upper limit.

Google script says - Exceeded maximum execution time

I am using the below script to delete duplicate rows from the google spreadsheet. The script was working good but as the data in the spreadsheet is being added daily, now the script is throwing "Exceeded maximum execution time" error. As I am new to scripting I don't understand what is my problem.
Could someone help me in solving this problem of mine.
function Deleteduplicates() {
var SpreadSheetKey = "My key";
var sheetD = SpreadsheetApp.openById(SpreadSheetKey).getSheetByName("Daily");
var sheetW = SpreadsheetApp.openById(SpreadSheetKey).getSheetByName("Weekly");
var dataD = sheetD.getDataRange().getValues();
var dataW = sheetW.getDataRange().getValues();
//Daily
var newDataD = new Array();
for(i in dataD){
var row = dataD[i];
var duplicate = false;
for(j in newDataD){
if(row.join() == newDataD[j].join()){
duplicate = true;
}
}
if(!duplicate){
newDataD.push(row);
}
}
//weekly
var newDataW = new Array();
for(i in dataW){
var row = dataW[i];
var duplicate = false;
for(j in newDataW){
if(row.join() == newDataW[j].join()){
duplicate = true;
}
}
if(!duplicate){
newDataW.push(row);
}
}
sheetD.clearContents();
sheetW.clearContents();
sheetD.getRange(1, 1, newDataD.length, newDataD[0].length).setValues(newDataD);
sheetW.getRange(1, 1, newDataW.length, newDataW[0].length).setValues(newDataW);
}
Conceptually, this should be quite a bit faster. I have not tried it on a large data set. The first version will leave the rows sorted as they were originally. The second version will be faster but will leave the rows sorted according to the columns from first to last on first text.
function Deleteduplicates() {
var SpreadSheetKey = "My key";
var ss = SpreadsheetApp.openById(SpreadSheetKey);
var sheetD = ss.getSheetByName("Daily");
var sheetW = ss.getSheetByName("Weekly");
var sheets = [sheetD, sheetW];
var toSs = {};
for(s in sheets) {
var data = sheets[s].getDataRange().getValues();
for(i in data){
// EDIT: remove commas from join("") for blank test
data[i].unshift(data[i].join(""),(1000000 + i).toString());
}
data.sort();
// remove blank rows -- Edit
var blank = 0;
while(data[blank][0].trim().length == 0) {blank++};
if(blank > 0) data.splice(0, blank);
// end Edit
var len = data.length - 1;
for(var x = len; x > 0; x-- ) {
if(data[x][0] == data[x-1][0]) {
data.splice(x, 1);
};
};
for(i in data) {
data[i].splice( 0, 1);
};
data.sort();
for(i in data) {
data[i].splice(0, 1);
};
toSs[sheets[s].getSheetName()] = data;
};
for(s in sheets) {
var data = toSs[sheets[s].getSheetName()];
sheets[s].clearContents();
sheets[s].getRange(1, 1, data.length, data[0].length).setValues(data);
}
}
Faster leaving rows sorted by join() created to test for duplicates
function Deleteduplicates() {
var SpreadSheetKey = "My key";
var ss = SpreadsheetApp.openById(SpreadSheetKey);
var sheetD = ss.getSheetByName("Daily");
var sheetW = ss.getSheetByName("Weekly");
var sheets = [sheetD, sheetW];
var toSs = {};
for(s in sheets) {
var data = sheets[s].getDataRange().getValues();
for(i in data){
// EDIT: remove commas from join("") for blank test
data[i].unshift(data[i].join(""));
}
data.sort();
// remove blank rows -- Edit
var blank = 0;
while(data[blank][0].trim().length == 0) {blank++};
if(blank > 0) data.splice(0, blank);
// end Edit
var len = data.length - 1;
for(var x = len; x > 0; x-- ) {
if(data[x][0] == data[x-1][0]) {
data.splice(x, 1);
};
};
for(i in data) {
data[i].splice( 0, 1);
};
toSs[sheets[s].getSheetName()] = data;
};
for(s in sheets) {
var data = toSs[sheets[s].getSheetName()];
sheets[s].clearContents();
sheets[s].getRange(1, 1, data.length, data[0].length).setValues(data);
}
}
Edited per Henrique's comment.
Edited 5/8: Remove blank rows(2 edited areas marked)
There is no problem with your script. It is just exceeding the "maximum execution time" allowed for any script (which is currently 6 minutes).
To workaround this problem you'll have to split your problem into "less than 6 minutes" parts.
For example, in your code you're clearing duplicates from 2 sheets. Trying creating two functions, one for each, and run them separately.
Also, there could be some performance enhancements that could make the script run under 6 minutes. For example, I'm not sure joining each row is the best way (performance-wise) to do an array comparison.
Creating a new array to re-set the data might not be optimal either, I'd probably go with a map verification, which is constant-time, instead of O(n^2) double array checking you're doing.
Bottom line, this is a limitation you have to live with in Apps Script. And any solution anyone proposes is just a workaround, that will also eventually fail if your data gets overly big.

Google Spreadsheet Script: "Cannot find function getRange in object Sheet" when creating a simple function

Sorry, for the stupid question, but I´ve searched the whole internet and I could not find a good Tutorial to learn how to program in Google SpreadSheet Script.
I want to make a very simple function just for practice.
function simplesum(input) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets();
var range = sheet.getRange(input);
var x = 0;
for (var i = 1; i <= range.getNumRows(); i++) {
for (var j = 1; j <= range.getNumColumns(); j++) {
var cell = range.getCell(i, j);
x += (cell.getValue());
}
}
return x;
}
I know I could use =sum() to do exactly the same thing. The idea here is to learn how to program.
When I try to use my function in a cell: (i.e: =simplesum((A1:A8)) it gives an Error saying: "TypeError: Cannot find function getRange in object Sheet. (line 4)"
What should I do?
And again, sorry for the dumb question....
In this case, you are implementing a Google Apps Script function as a custom function, invoked in a spreadsheet cell.
When you pass a range to a custom function invoked in a spreadsheet cell, you are not passing a range object or a range reference, but rather a 2-D Javascript array of values. So your custom function should just process that array.
function simplesum(input)
{
var x = 0;
for (var i = 0; i < input.length; i++)
{
for (var j = 0; j < input[0].length; j++)
{
x += input[i][j];
}
}
return x;
}
This is working :
function sum(input) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet(); // your mistake : getSheets()
var range = sheet.getRange(input);
var x = 0;
for (var i = 1; i <= range.getNumRows(); i++) {
for (var j = 1; j <= range.getNumColumns(); j++) {
var cell = range.getCell(i, j);
x += cell.getValue();
}
}
return x;
}
function main () // Yes, I am a former C-programmer ...
{
var s = sum ("A1:B3"); // Notice the quotes. A string must me entered here.
Logger.log('s = ' + s);
}
var sheet = ss.getSheets();
returns an Array of sheets, meaning sheets.getRange(input) will throw that error. Try this instead:
var sheet = ss.getSheets()[0];
which selects the first sheet of the array of sheets. Google has some decent documentation for this. For example, here's its documentation on getRange(). Note that it uses ss.getSheets()[0] as well. Hope this helped!

Unable to filter data from other spreadhseet paste in master sheet based on dates in google spreadsheet

First of all thank you for the efforts made in this site. As an individual and a beginner i have learnt from my errors made. Thanks for all who have contributed & extended their support.
Thanks for this.
Here is a small program written which is not working (no output)seen , i have tried it in many ways but in vain. please help me to find a solution for this.
the aim of this program was to filter the data from 4 sheets and paste into current sheet (master). this filter is based on date values.
Conditions of dates are taken from the master sheet in columns in (b2 & d2)dates. this are to be filtered out based in column no.18 which has dates in client sheets.
function myFunction3() {
var source = ['0AjkkHlm3kCphdGhSWnlxWmFsakZ2aFhMSHl6SlF3M1E',
'0AjkkHlm3kCphdHY2aXpjTVJEMlFRYVBST0ZPYzNwRFE',
'0AjkkHlm3kCphdEc5ZHFpeHVlc241SlFKWGJDeXFKLXc',
'0AjkkHlm3kCphdG9WVjVRRnQ3RlFlcllhd1JGallXVmc'];
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get start date from sheet
var sDate = ss.getSheetByName('123').getRange("B2").getValue();
// get end date from sheet
var eDate = ss.getSheetByName('123').getRange("D2").getValue();
// days between
var Dura = ss.getSheetByName('123').getRange("E1").getValue();
var codes = new Array();
for (var k = 0; k < Dura; k++){
var d = new Date(sDate);
d.setDate(d.getDate()+ k);
codes[k] = d;
}
var numCodes = codes.length;
var copied = [];
for (var k = 0; k < numCodes; k++) {
copied[k] = [];
}
//get data from external sheets for comparision
for (var i = 0; i < source.length; i++) {
var tempCopy = SpreadsheetApp.openById(source[i]).getSheetByName('Footfall-Format').getDataRange().getValues();
// comparision starts
for (var j = 0; j < tempCopy.length; j++) {
var codeIndex = codes.indexOf(tempCopy[j][5]);
if (codeIndex > -1) copied[codeIndex].push(tempCopy[j]);
}
}
var sheets = SpreadsheetApp.getActive().getSheets();
for (var m = 0; m < numCodes; m++) {
if (copied[m][0] != undefined) {
var gensheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('123');
gensheet.getRange(5, 1, 1500, 18).clear({contentsOnly:true});
gensheet.getRange(5, 1, copied[m].length, copied[m][0].length).setValues(copied[m]);
}
}
}
The fundamental problem is that you are comparing objects for equality - in this case, you're comparing Date objects. Even when the date represented by two of these objects is the same, the object comparison comes up false unless you're actually referencing the same object. You can read more about this in Compare two dates with JavaScript.
Here's a simple change to your script, using toDateString(), that will ensure your codes[] array contains string values that can be compared with values in tempCopy[j][5].
function myFunction3() {
var source = ['0AjkkHlm3kCphdGhSWnlxWmFsakZ2aFhMSHl6SlF3M1E',
'0AjkkHlm3kCphdHY2aXpjTVJEMlFRYVBST0ZPYzNwRFE',
'0AjkkHlm3kCphdEc5ZHFpeHVlc241SlFKWGJDeXFKLXc',
'0AjkkHlm3kCphdG9WVjVRRnQ3RlFlcllhd1JGallXVmc'];
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get start date from sheet
var sDate = ss.getSheetByName('123').getRange("B2").getValue();
// get end date from sheet
var eDate = ss.getSheetByName('123').getRange("D2").getValue();
// days between
var Dura = ss.getSheetByName('123').getRange("E1").getValue();
var codes = new Array();
for (var k = 0; k < Dura; k++){
var d = new Date(sDate);
d.setDate(d.getDate()+ k);
codes[k] = d.toDateString(); //***** Make array of Strings, not Dates
}
var numCodes = codes.length;
var copied = [];
for (var k = 0; k < numCodes; k++) {
copied[k] = [];
}
//get data from external sheets for comparision
for (var i = 0; i < source.length; i++) {
var tempCopy = SpreadsheetApp.openById(source[i]).getSheetByName('Footfall-Format').getDataRange().getValues();
// comparision starts
for (var j = 4; j < tempCopy.length; j++) { // start at 4 to skip headers
if (typeof tempCopy[j][5] != "object") break; // skips strings, but could improve
// Search for String match of date from input record
var codeIndex = codes.indexOf(tempCopy[j][5].toDateString());
if (codeIndex > -1) copied[codeIndex].push(tempCopy[j]);
}
}
// This part has bugs... each day overwrites the previous
var sheets = SpreadsheetApp.getActive().getSheets();
for (var m = 0; m < numCodes; m++) {
if (copied[m][0] != undefined) {
var gensheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('123');
gensheet.getRange(5, 1, 1500, 18).clear({contentsOnly:true});
gensheet.getRange(5, 1, copied[m].length, copied[m][0].length).setValues(copied[m]);
}
}
}
As #Serge points out, there are other problems in this code.
d.getDate()+ k does not handle month-end, so you need to do that yourself.
The last part of your script that handles the output via setValues() needs to be debugged. As it is, each day overwrites the previous day's values from the copied[] array. I'm not sure what requirement you were trying to meet with this, so I left it alone, but it needs attention.

Categories

Resources