I need a quick function to loop through the rows of one of my sheets so I constructed this:
function getTotalsOf(name, type) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(name);
var data = sheet.getDataRange().getValues();
var total = 0.00;
for (var i = 0; i < data.length; i++) {
if (data[i][0]== "") break;
if (data[i][2] == type) {
var temp = (Number(data[i][4]) + Number(data[i][5]) + Number(data[i][6])) * data [i][3];
total = Number(total) + total;
}
}
return total;
}
SpreadsheetApp.getActiveSpreadsheet() seems to return null when I try to execute the function. It also returns null when I try to get the spreadsheet by id. Am I missing something?
EDIT:
I retried using my spreadsheet id (https://docs.google.com/spreadsheets/d/STRINGFROMHERE/edit#gid=1235572150) with the same results.
function getTotalsOf(name, type) {
var sheet = SpreadsheetApp.openById("**STRINGFROMURL**").getSheetByName(name);
var data = sheet.getDataRange().getValues();
var total = 0.00;
for (var i = 0; i < data.length; i++) {
if (data[i][0]== "") break;
if (data[i][2] == type) {
var temp = (Number(data[i][4]) + Number(data[i][5]) + Number(data[i][6])) * data [i][3];
total = Number(total) + temp;
}
}
return total;
}
I think you just missed a few concepts with regard to looping through spreadsheets. Hope this demo will help and get you what you want. Always remember that rows and cols in spreadsheets starts with 1,1 not 0,0.
function loopThroughCells() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = sheet.getDataRange().getValues();
Logger.log(data[0].length);
for(var i=0; i< data.length; i++){
for(var j=0; j<data[i].length; j++){
if(data[i][j] == "HELIOS"){
if (data[i][j]== "") break;
Logger.log("HELIOS data found in range "+ (i+1) + " "+(j+1));
}
}
}
Log
[17-07-07 21:45:17:157 HKT] HELIOS data found in range 3 3
Related
I made a script that works properly (does what I want it to), however, it's painfully slow and at this pace, it will finish in about 20 days. I can't wait for 20 days and I'm not good enough at this to make it faster on my own.
Here's a brief description of the task:
Masterlist - it's a sheet with 23 columns and 29000+ rows.
Seed - it's an empty sheet that I'm to copy the Masterlist to.
Duplicates - it's an empty sheet where I will store any duplicate rows.
The process:
Get the first line from Masterlist. Check if line already in Seed. If line not in Seed, add line. If line already in Seed, add line to Duplicates. Either way, delete the original line from the Masterlist.
The definition of duplicate:
Each line has an emails column. Column can be either a single email address, or multiple email addresses separated by "; ". If an email is found within line in Masterlist and already exists within line in Seed, this whole line is considered a duplicate.
Example:
"aaa#gmail.com" is not a duplicate of "a#gmail.com; aa#gmail.com"
"bbb#gmail.com" is a duplicate of "b#gmail.com; bbb#gmail.com"
Furthermore, if the emails cell is empty in the Masterlist, this is not considered a duplicate.
Here comes my code - it works but is not fast enough.
function getSheet(name){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(name);
return sheet;
}
function getRowByID(sheet, rowID) {
var range = sheet.getRange(rowID, 1, 1, 23);
var value = range.getValues();
return [range, value];
}
//main executes the entire thing
function main(){
var sourceSheet = getSheet('Masterlist');
var targetSheet = getSheet('Seed');
var remainingSheet = getSheet('Duplicates');
var counter = sourceSheet.getLastRow();
var start = new Date();
while(counter >= 2){
var sourceLine = getRowByID(sourceSheet, 2)[1];
var duplicates = checkEmailMatch(sourceLine, targetSheet);
if(duplicates == 0){
targetSheet.appendRow(sourceLine[0]);
sourceSheet.deleteRow(2);
}
else{
remainingSheet.appendRow(sourceLine[0]);
sourceSheet.deleteRow(2);
}
counter--;
}
}
//iterates through existing lines in the Seed sheet (locates the email cell and reads its contents)
function checkEmailMatch(row, seed){
var sourceEmail = row[0][7];
var counter = seed.getLastRow();
var result = [];
if(!counter){
return 0;
}
else{
var j = 0;
var i = 2;
for(i; i <= counter; i++){
var seedLine = getRowByID(seed, i)[1];
var seedEmail = seedLine[0][7];
if(!seedEmail){}
else if(compareEmails(seedEmail, sourceEmail) == true) {
result[j] = i;
j++;
}
}
return result;
}
}
//Compares each email in Masterlist ("; " separated) with each email in Source ("; " separated)
function compareEmails(emailSeedCell, emailSourceCell){
var seedEmails = emailSeedCell.split("; ");
var sourceEmails = emailSourceCell.split("; ");
for(var i = 0; i < seedEmails.length; i++){
for(var j = 0; j < sourceEmails.length; j++){
if(seedEmails[i] == sourceEmails[j]) return true;
}
}
return false;
}
Please help me - if you need any additional info, I'd be happy to provide! Please note that this is my third script ever, so any feedback is welcome!
Thanks to everyone who chipped in to help, I managed to come up with this code that reduced the execution time more than 10000 times! Thanks, everyone - here's the code:
function sheetToArray(name){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(name);
var counter = sheet.getLastRow();
var columns = sheet.getLastColumn();
var array = sheet.getRange(2, 1, counter, columns).getValues();
return array;
}
function compareEmails(emailSeedCell, emailSourceCell){
var seedEmails = emailSeedCell.split("; ");
var sourceEmails = emailSourceCell.split("; ");
var result = false;
for(var i = 0; i < seedEmails.length; i++){
for(var j = 0; j < sourceEmails.length; j++){
if(seedEmails[i] == sourceEmails[j]) result = true;
}
}
return result;
}
function save2DArrayToSpreadsheet(name, array){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(name);
sheet.getRange(2, 1, array.length, array[0].length).setValues(array);
}
function main(){
var masterArray = sheetToArray('Masterlist');
var seedArray = [];
var duplicateArray = [];
for(var i = 0; i < masterArray.length; i++){
Logger.log(i);
if(!seedArray.length){
seedArray.push(masterArray[i]);
}
else if(!masterArray[i][7]){
seedArray.push(masterArray[i]);
}
else{
var result = false;
for(var j = 0; j < seedArray.length; j++){
if(compareEmails(seedArray[j][7], masterArray[i][7]) == true){
result = true;
}
}
if(result == true){
duplicateArray.push(masterArray[i]);
}
else{
seedArray.push(masterArray[i]);
}
}
}
save2DArrayToSpreadsheet("Seed", seedArray);
save2DArrayToSpreadsheet("Duplicates", duplicateArray);
}
The following code will run very slow in my Google Sheets because my getRange is too large. Is there a way to only loop through the columns that are merged? I only want the for loop to get the number of columns in "yourRange" that are merged.
function getUpfrontCosts() {
var sheet = SpreadsheetApp.getActive().getSheetByName('LPB_COST');
var cl , count=0;
var yourRange = sheet.getRange("H13:UV13");
for (var i = 1; i < yourRange.getNumColumns()+1; i++)
{
cl=yourRange.getCell(1, i);
if (cl.isPartOfMerge()){
if (cl.offset(15, 0).getBackground() == "#ff8300" && cl.getMergedRanges()[0].getCell(1, 1).getValue()=='Upfront Costs') {
count = count + cl.offset(15, 0).getValue();
}
else {
}
} else {
}
}
return count;
};
The second code is how I am trying to turn a string to a range. I am getting "Cell reference out of range" error
How can I change cl to not be a string and be a range?
function getUpfrontCosts()
{
var sheet = SpreadsheetApp.getActive().getSheetByName('LPB_COST');
var destSheet = SpreadsheetApp.getActive().getSheetByName('Top Level PN');
var cl , count=0;
var yourRange = sheet.getRange("I13:UZ13");
var mergedRanges = yourRange.getMergedRanges();
for (var i = 0; i < mergedRanges; i++){
}
var newRange = sheet.getRange(mergedRanges[i].getA1Notation());
Logger.log(newRange.getA1Notation());
for (var i = 0; i < newRange.getNumColumns()+1; i++){
cl=newRange.getCell(1, i);
Logger.log(newRange.getA1Notation());
if (cl.offset(15, 0).getBackground() == "#ff8300" && cl.getValue()=='Upfront Costs') {
count = count + cl.offset(15, 0).getValue();
}
else {
}
}
return count;
};
This is the line with the error
cl=newRange.getCell(1, i);
if you want to use merge cell range, you can do this:
function UntitledMacro1()
{
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
var cl , count=0 ;
var yourRange = sheet.getRange("I13:UZ13");
var bb = yourRange.getMergedRanges();
for (a=bb[0].getColumn();a<bb[0].getLastColumn()+1;a++)
{
//Your actual columns from I13, for first merge range, here your cl,
//but if your range is ("13:13"), you don't need
//-sheet.getRange("I13").getColumn()+1
cl=yourRange.getCell(1, a-sheet.getRange("I13").getColumn()+1);
//For your offset 15
Logger.log(cl.offset(15, 0).getValue());
//on so on
}
};
I've tried a bunch of solution but it always end with Cell ref is out of range.
I thought that GetDataRange() & GetLastRow() would cover the last non-blank cell but I'm stuck. I've tried to hardcode my cell values (from X to Y) but nothing seems to change.
var course;
var searchingCol;
function CalculateFurnitureList()
{
GetCourseString();
FindColumnNumber(course);
GetSpecialFurnituresValue();
}
function GetCourseString()
{
var activeSheet = SpreadsheetApp.getActiveSheet();
var courseType = activeSheet.getRange("A2").getValue();
Browser.msgBox('Generate furniture list for ' + courseType + ' course');
course = courseType;
}
function FindColumnNumber(ct)
{
var furnitureSheet = SpreadsheetApp.getActive().getSheetByName("MaterielCours");
var range = furnitureSheet.getDataRange();
var width = range.getWidth();
//Search every cell in the designated area to find the course name
for (var i = 1; i <= width; i++)
{
var data = range.getCell(1,i);
if (data.getValue() == ct)
{
Browser.msgBox('Trouvé ! ' + data.getValue() + ' se trouve en : ' + i +':1');
searchingCol = i;
}
}
}
function GetSpecialFurnituresValue()
{
Browser.msgBox(searchingCol);
var furnitureSheet = SpreadsheetApp.getActive().getSheetByName("MaterielCours");
var range = furnitureSheet.getDataRange();
var length = range.getLastRow();
for (var i = 1; i < length; i++)
{
var data = range.getCell(searchingCol, i);
if (data.getValue() !== "")
{
// Do nothing
}
else
{
if (data == "Abs")
{
Browser.msgBox('NTM');
}
else
{
// Calculate value
}
}
}
}
It's
var data = range.getCell(searchingCol, i);
in GetSpecialFurnituresValue() which causes the problem.
The weird thing is that the same process in FindColumnNumber is working perfectly.
(editing my original answer - I must have been tired!) In the function GetSpecialFurnitureValues, there are a few things.
The for loop uses i < length, but it should be <= or you will miss the last row:
// for (var i = 1; i < length; i++)
for (var i = 1; i <= length; i++)
Your column/rows were simply reversed. :)
// var data = range.getCell(searchingCol, i);
var data = range.getCell(i, searchingCol);
Hope that helps!
i use function script in google spread sheet.
this function is used maybe... 150,000 cell.
my question is... infinite loading.
when i use my custom function in sheet, infinite loading appear
how can i resolve that?
here is my script code :
function s2hex(str1){
var s2hex = 0;
var byte_check = 0;
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("tbl");
var range1 = sheet1.getRange(1, 1, sheet1.getMaxRows(),
sheet1.getMaxColumns());
for(var i = 0; i < str1.length; ++i){
for(var k = 1; k < sheet1.getMaxRows(); ++k){
if(str1[i] == range1.getCell(k, 2).getValue() || str1[i] == " "){
s2hex = s2hex + 1;
byte_check = 1;
break;
}
}
if(byte_check == 0){
s2hex = s2hex + 2;
}
byte_check = 0;
}
return s2hex;
};
getMaxRows() will return all rows in the sheet. If you have content in A1:A10 and there are empty rows from A10 to A100000. It'll return 100,000 instead of 10. Use getLastRow() instead.
If you're only using the second column,Specify 2 as column number and 1 instead of getMaxColumns()
Use getValues() to get a single array instead of making multiple calls to spreadsheet.
Try Modifying from:
var range1 = sheet1.getRange(1, 1, sheet1.getMaxRows(), sheet1.getMaxColumns());
for(var i = 0; i < str1.length; ++i)
{
for(var k = 1; k < sheet1.getMaxRows(); ++k)
{
if(str1[i] == range1.getCell(k, 2).getValue() || str1[i] == " ")
{
s2hex = s2hex + 1;
To
var lr =sheet1.getLastRow(); //One last row call to be used in multiple places
var range = sheet1.getRange(1, 2, lr,1);
var range1 = range.getValues(); //get all values in a single call
for(var i = 0; i < str1.length; ++i)
{
for(var k = 0; k < lr; ++k) //k to 0.Array index start at 0
{
if(str1[i] == range1[k][0] || str1[i] == " ") //Check the 2D value of already retrieved array
{
s2hex = s2hex + 1;
150,000 function calls will slow down any sheet.
That said, your function is in dire need of optimization: you need to remove the need for extensive calls over the Spreadsheet interface. Each such call can take as much as 0.1 second. You can do this simply, by reading the entire range of your values to search with getValues(), and then iterating that in the same manner as you do currently.
I have a google spreadsheet with two columns corresponding to lessons: the first with names of the porfessors (occasionally repeating themselves) and the second with numbers (number of hours). I would like to have as output two columns, the first with the names of the porfessors and the second with the sum of all the hours
I tried with the following code, but it seems to give me back two arrays with the initial colums, as if the condition if (names[names.length-1] == namesColumn[i]) is never met.
What am I doing wrong?
function resumeProfessors() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];
var namesColumn = sheet.getRange("C4:C31").getValues();
var lessonsColumn = sheet.getRange("G4:G31").getValues();
var names = [];
names.length = 0;
var lessons = [];
lessons.length = 0;
namesColumn.sort();
for (var i = 0; i < namesColumn.length; i++) {
if (names[names.length-1] == namesColumn[i]){
lessons[lessons.length-1] = lessons[lessons.length-1] + lessonsColumn[i];}
else{
sheet.getRange(i+4, 9).setValue(names[names.length-1] + namesColumn[i]);
names[names.length] = namesColumn[i];
lessons[lessons.length] = lessonsColumn[i];
};}
writeResume(names, lessons);
}
Ty
Given your use-case, I'd recommend using a Pivot table or the =QUERY formula.
However, assuming your input sheet looks something like this -
And the expected output is something like this -
You can try the below code -
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var input = ss.getSheetByName('Sheet1');
var output = ss.getSheetByName('Sheet2');
var inputValues = input.getDataRange().getValues();
Logger.log(inputValues)
for (var i = 1; i < inputValues.length; i++) {
var name = inputValues[i][0];
var totalHours = [];
for (var j = 0; j < inputValues.length; j++) {
var hours = inputValues[j][1];
if (name == inputValues[j][0]) {
totalHours.push(inputValues[j][1]);
}
}
var outputValues = output.getDataRange().getValues();
var newEntry = true;
for (var k = 0; k < outputValues.length; k++) {
if (name == outputValues[k][0]) {
newEntry = false;
}
}
if (newEntry) {
output.appendRow([name,totalHours.reduce(function(a, b) {return a + b})]);
}
}
}
Hope this helps.