I am trying to compare string values that I have obtained from my google spreadsheet.
My Months appear in this format " Jun13", "Aug13" etc. and my script has trouble comparing the values when they are in such formats
var data = ss.getRange(2,2,ss.getLastRow(),3).getValues();
var ListOfMonths = new Array();
for(var i = 0; i < data.length; ++i){
var row = data[i][0];
var duplicate = false;
for(j in ListOfMonths){
if(row.toString.match(ListOfMonths[j][0])){
duplicate = true;
}
if(!duplicate){
ListOfMonths.push(row);
}
}
}
Could someone help me with this issue of comparing strings? I also tried == and === but they all don't work
try that:
function test(){
var ss = SpreadsheetApp.getActive().getSheetByName("month test");
var data = ss.getRange(2,2,ss.getLastRow(),3).getValues();
var ListOfMonths = new Array();
for(var i in data){
var row = data[i][0].toString();
if(ListOfMonths.indexOf(row)==-1){
Logger.log(row+" is not referenced. Adding it");
ListOfMonths.push(row);
}
}
Logger.log(ListOfMonths);
}
Harold's code is more efficient than yours but it has a different logic... if you're interrested to know why your code didn't work here is a version that takes the same approach as yours but with a few bug corrections that make it work.
Shortly described, you pushed the value at a wrong place in the loop and you forgot to add a first item to have at least one element in your second array. You didn't use match the right way, match returns the 'common' part in the string, not a boolean.
Here is the code :
function myFunction() {
var ss = SpreadsheetApp.getActiveSheet();
var data = ss.getRange(2,2,ss.getLastRow(),3).getValues();
var ListOfMonths = new Array();
ListOfMonths.push(data[0])
for(var i = 0; i < data.length; ++i){
var row = data[i];
var duplicate = false;
for(j in ListOfMonths){
var item = ListOfMonths[j][0];
if(row.toString().match(item) == item){
duplicate = true;
break;
}
}
if(!duplicate && i<data.length-1){
ListOfMonths.push(row);
}
}
Logger.log(ListOfMonths);
}
Is there any reason you can't use the '==' operator?
if ( "April" == ListOfMonths[j][0] ) {
Logger.log("Match!");
}
Just use '==' operator:
if(myString != ''){
//do something
}
Enjoy!
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);
}
I'm trying to get a SCORESHEET to populate from a REPORTSHEET, using a REFERENCESHEET to collate search terms and destination cells.
The script I'm running is as below. The idea is that the script finds searchDate's in the REFERENCESHEET and uses them to locate data columns in the REPORTSHEET:
function superAuto() {
var report = SpreadsheetApp.openById('REPORTSHEET');
var reportData = report.getDataRange().getValues();
var reference = SpreadsheetApp.openById('REFERENCESHEET');
var referenceData = reference.getDataRange().getValues();
var scorecard = SpreadsheetApp.openById('SCORESHEET');
var scorecardData = scorecard.getDataRange().getValues();
var tExpenses = "Total Expenses";
for(n=0;n<referenceData.length;++n){
var searchDate = referenceData[n][0] ;
Logger.log (searchDate)
}
var column = columnfinder(searchDate);
for (var a = 0; a < referenceData.length; a++) {
var refRow = referenceData[a];
for (var i = 0; i < reportData.length; i++) {
var row = reportData[i];
if (row[0] == tExpenses && refRow[0] == searchDate) {
scorecard.getRange(refRow[5]).setValue(row[column]);
}
}
}
}
function columnfinder(find) {
var report = SpreadsheetApp.openById('REPORTSHEET');
var reportData = report.getDataRange().getValues();
var reference = SpreadsheetApp.openById('REFERENCESHEET');
var referenceData = reference.getDataRange().getValues();
for(var j=0, jLen=reportData.length; j<jLen; j++) {
for(var k=0, kLen=reportData[0].length; k<kLen; k++) {
if(find == reportData[j][k]) {
Logger.log(k);
return (k);}
}
}
}
Broadly speaking, the code works, as if I define searchDate as one of the terms I'm looking for (e.g. Jan-21) it all works fine. The issue is that it doesn't seem to be doing so when finding multiple search terms - and therefore populating multiple rows - as per:
for(n=0;n<referenceData.length;++n){
var searchDate = referenceData[n][0] ;
Logger.log (searchDate)
}
The log tells me that it's finding searchDate's in the REFERENCESHEET, but it's not able to run them through function columnfinder (I get no logs for the second logger).
I suspect the answer lay somewhere in an earlier great answer I received to an earlier version of this idea - How to return multiple column values for setValue - but I've not been able to make it fit. Any thoughts?
EDIT: Please find a sample REFERENCESHEET & REPORTSHEET for more info:
The log tells me that it's finding searchDate's in the REFERENCESHEET,
but it's not able to run them through function columnfinder (I get no
logs for the second logger)
You don't execute columnfinder inside the for loop.
Try this:
for(n=0;n<referenceData.length;++n){
var searchDate = referenceData[n][0] ;
Logger.log(searchDate);
columnfinder(searchDate); // modified code
}
and you will get both logs.
Sorry if I misunderstood your question.
You need to use have it assigned to array since you are returning possible multiple columns/dates:
function superAuto() {
var report = SpreadsheetApp.openById('REPORTSHEET');
var reportData = report.getDataRange().getValues();
var reference = SpreadsheetApp.openById('REFERENCESHEET');
var referenceData = reference.getDataRange().getValues();
var scorecard = SpreadsheetApp.openById('SCORESHEET');
var scorecardData = scorecard.getDataRange().getValues();
var tExpenses = "Total Expenses";
var searchDates = [];
for (n = 0; n < referenceData.length; ++n) {
searchDates.push(referenceData[n][0])
}
var columns = columnfinder(searchDates);
columns.forEach(function (column, index) {
referenceData.forEach(function (refRow) {
reportData.forEach(function (row) {
if (row[0] == tExpenses && refRow[0] == searchDates[index]) {
scorecard.getRange(refRow[5].toString()).setValue(row[column]);
}
});
});
});
}
function columnfinder(dates) {
var report = SpreadsheetApp.openById('REPORTSHEET');
var reportData = report.getDataRange().getValues();
var reference = SpreadsheetApp.openById('REFERENCESHEET');
var referenceData = reference.getDataRange().getValues();
var columns = [];
dates.forEach(function (date) {
reportData.forEach(function (row, i) {
row.forEach(function (col, i) {
if (date == reportData[i][j]) {
columns.push(reportData[i][j]);
}
});
});
});
return columns;
}
I changed some variables into proper variable names to avoid confusion.
Additionally, if it doesn't work, you might need to share a proper visualization of the data, or better yet, provide some sample sheet we can work on for us to be able to give you a better and tested answer.
Thanks Marios, that's twice in a week. Much appreciated.
Slight adaptation, in order for it to populate SCORECARD I needed to bring everything into the for loop, as below:
for(n=0;n<referenceData.length;++n){
var searchDate = referenceData[n][0] ;
Logger.log (searchDate)
var column = columnfinder(searchDate);
for (var a = 0; a < referenceData.length; a++) {
var refRow = referenceData[a];
for (var i = 0; i < reportData.length; i++) {
var row = reportData[i];
if (row[0] == tExpenses && refRow[0] == searchDate) {
scorecard.getRange(refRow[5]).setValue(row[column]);
}
}
}
}
I have a spreadsheet with multiple rows and columns. Two columns (column 3&4) are filled with text. I want to clean the text from this two columns and delete every specific characters (newlines, comma, exclamation point, quote,etc...). So I wrote the following script :
function testwoD() {
var input = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Raw_data");
var output = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Raw_data");
var row_count = input.getLastRow()
var col_count = input.getLastColumn();
raw_data = input.getRange(1, 1,row_count,col_count).getValues()
temp3 = []
for (var i = 0; i < row_count; i++) {
var punctRE = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,\-.\/:;<=>?#\[\]^_`{|}~\r\n|\n|\r]/g;
var spaceRE = /\s+/g;
temp3.push(raw_data[i][4].toString().replace(punctRE, '').replace(spaceRE, ' '));
}
temp4 = []
for (var i = 0; i < row_count; i++) {
var punctRE = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,\-.\/:;<=>?#\[\]^_`{|}~\r\n|\n|\r]/g;
var spaceRE = /\s+/g;
temp4.push(raw_data[i][3].toString().replace(punctRE, '').replace(spaceRE, ' '));
}
var toAddArray3 = [];
for (i = 0; i < temp3.length; ++i){
toAddArray3.push([temp3[i]]);
}
var toAddArray4 = [];
for (i = 0; i < temp4.length; ++i){
toAddArray4.push([temp4[i]]);
}
output.getRange(1, col_count-13,row_count,1).setValues(toAddArray3);
output.getRange(1, col_count-14,row_count,1).setValues(toAddArray4);
}
It's working but It's very complicated and confusing. I made it step-by-step so even myself have some difficulties to really explain it.
Is there a way to significantly improve it ?
Best,
Simon.
DRY! - Do not repeat yourself.
Another popular idiom should be UMNF - Use map not for.
Putting everything into its own function encapsulates functionality and puts the focus on what you want to do with the data at each level rather than bookkeeping indices and subscripts.
function cleanColumns() {
var input = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var output = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var raw_data = input.getDataRange().getValues();
var columnsToClean = [3,4];
function cleanText(t) {
var punctRE = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,\-.\/:;<=>?#\[\]^_`{|}~\r\n|\n|\r]/g;
var spaceRE = /\s+/g;
return t.toString().replace(punctRE, "").replace(spaceRE, " ");
};
function cleanColumn(col) {
return raw_data
.map(function(row) {return row[col];})
.map(cleanText)
.map(function(row) {return [row];})
};
function cleanAndWrite(col) {
var data = cleanColumn(col);
output.getRange(1, col + 1, data.length, 1).setValues(data);
}
columnsToClean.forEach(cleanAndWrite);
}
How do I get the row and column indices of the cell containing a value I'm looking for?
Here's an example of two sheets, "Grave" and "Data_grave":
My code, below, should...
First, get a specific value in sheet "Grave" (in example value is - "Win").
Find the number of the row & column with this value in sheet "Data_grave".
Finally, it should write some data ("wow") near the found value "Win" (from column+1).
However, I receive an error message at line 17 (the line following my search loops):
Can't convert 4,4 to (class)
How do I solve that?
function myFind() {
var ss = SpreadsheetApp.getActive(), rowNum = [], collNum = [];
var findData = ss.getSheetByName('Grave').getRange("A2").getValue();
var searchData = ss.getSheetByName('Data_grave').getDataRange().getValues();
for(var i=1, iLen=findData.length; i<iLen; i++) {
for(var j=0, jLen=searchData.length; j<jLen; j++) {
for(var k=0, kLen=searchData[0].length; k<kLen; k++) {
var find = findData;
if(find == searchData[j][k]) {
rowNum.push([j+1]);
collNum.push([k+2]);
}
}
}
}
ss.getSheetByName('Data_grave').getRange(rowNum,collNum).setValue("wow");
}
As Adelin commented: the error message is indicating that you are not using .getRange(rowNum,collNum) properly. That method expects two numbers, but you're providing it two arrays.
When you've "found" the cell you're searching for, instead of push() (which treats rowNum and colNum as arrays), you simply want to use:
var rowNum = j+1;
var colNum = k+2;
You could also use a boolean found as an additional exit condition for all your loops, to stop searching upon success.
function myFind() {
var ss = SpreadsheetApp.getActive(), rowNum = [], collNum = [];
var findData = ss.getSheetByName('Grave').getRange("A2").getValue();
var searchData = ss.getSheetByName('Data_grave').getDataRange().getValues();
var found = false;
for(var i=1, iLen=findData.length; i<iLen && !found; i++) {
for(var j=0, jLen=searchData.length; j<jLen && !found; j++) {
for(var k=0, kLen=searchData[0].length; k<kLen && !found; k++) {
var find = findData;
if(find == searchData[j][k]) {
var rowNum = j+1;
var collNum = k+2;
found = true;
}
}
}
}
ss.getSheetByName('Data_grave').getRange(rowNum,collNum).setValue("wow");
}
I have an app through google docs that I use to record student attendance to my after school tutoring center. I have had higher than usual attendance and my code is not efficient enough to run before timing out. Please assist me in editing my code so that it runs more effectively.
I have to note that I am an absolute beginner, so if this is inappropriately posted, please give me constructive feedback on how to get help with this problem. And I apologize in advance for the (I assume) horrible code you are about to see - it's the best I could do and I worked very hard to create this.
Thank you.
Code:
function updateAttendance(){
var itemSpreadsheetKey = '';
var openedSS = SpreadsheetApp.openById(itemSpreadsheetKey);
var sheetStudentNames = openedSS.getSheetByName("StudentNames");
var sheetDailyData = openedSS.getSheetByName("DailyData");
var app = UiApp.getActiveApplication();
var ss = SpreadsheetApp.openById(itemSpreadsheetKey);
var dailyDataSheet = ss.getSheetByName("DailyData");
var studentNameSheet = ss.getSheetByName("StudentNames");
var dailyData = dailyDataSheet.getDataRange();
var studentNames = studentNameSheet.getDataRange();
var dailyLastRow = dailyData.getLastRow();
var studentNamesLastRow = studentNames.getLastRow();
var studentNamesLastColumn = studentNames.getLastColumn();
var dailyDataNamesArray = sheetDailyData.getRange(2, 1, dailyLastRow).getValues();
for (var i=1; i<=dailyDataNamesArray.length; i++) {
if (i != ""){
var dailyTime = dailyData.getCell(i, 5).getValue();
for (var j=2; j<=studentNamesLastRow; j++) {
var today = dailyData.getCell(1, 8).getValue();
if (dailyData.getCell(i, 1).getValue() == studentNames.getCell(j, 1).getValue()) {
studentNames.getCell(1, studentNamesLastColumn).offset(0, 1).setValue(today);
studentNames.getCell(j, studentNamesLastColumn).offset(0, 1).setValue(dailyTime);
}
}
}
}
return app;
}
If you want your script to go faster you must use array manipulation instead of all these range.setValue() .
Change all the values in a global array (a pair of actually) and when you're done just write these arrays to their respective sheets.
The difference will be enormous ! believe me ;)
All this is pretty well explained in the documentation about best practices.
Here is a "translation" of your code, I'm not sure I didn't make any error in transposition but I have no way to check without knowing what is in your sheets.
Just remember that arrays are 0 indexed while ranges start at 1.
function updateAttendance(){
var itemSpreadsheetKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx';
var openedSS = SpreadsheetApp.openById(itemSpreadsheetKey);
var sheetStudentNames = openedSS.getSheetByName("StudentNames");
var sheetDailyData = openedSS.getSheetByName("DailyData");
var app = UiApp.getActiveApplication();
var ss = SpreadsheetApp.openById(itemSpreadsheetKey);
var dailyDataSheet = ss.getSheetByName("DailyData");
var studentNameSheet = ss.getSheetByName("StudentNames");
var dailyData = dailyDataSheet.getDataRange();
var studentNames = studentNameSheet.getDataRange().getValues();
var dailyLastRow = dailyData.getLastRow();
var studentNamesLastRow = studentNames.getLastRow();
var studentNamesLastColumn = studentNames.getLastColumn();
var dailyDataNamesArray = sheetDailyData.getDataRange().getValues();
for (var i = 1 ; i < dailyDataNamesArray.length; i++) {
if (i != ""){
var dailyTime = dailyDataNamesArray[i][4];
for (var j=1; j<studentNamesLastRow; j++) {
var today = dailyDataNamesArray[0][7];
if (dailyDataNamesArray[i][0] == studentNames[i][0]) {
studentNames[0][studentNamesLastColumn+1] = today;
studentNames[j][studentNamesLastColumn+1] = dailyTime;
}
}
}
}
studentNameSheet.getRange(1,1,studentNames.length,studentNames[0].length).setValues(studentNames):
dailyDataSheet.getRange(1,1,dailyDataNamesArray.length,dailyDataNamesArray[0].length).setValues(dailyDataNamesArray):
return app;
}
Replace the code after the var dailyDataNamesArray line with the following. It's a bit of a guess, so I hope for you that it works:
var todayCell1 = studentNames.getCell(1, studentNamesLastColumn).offset(0, 1);
for (var i=1; i<=dailyDataNamesArray.length; i++) {
if (i != ""){
var dailyTime = dailyData.getCell(i, 5).getValue();
var dailyVar2 = dailyData.getCell(i, 1).getValue();
for (var j=2; j<=studentNamesLastRow; j++) {
var today = dailyData.getCell(1, 8).getValue();
if ( dailyVar2 == studentNames.getCell(j, 1).getValue()) {
todayCell1.setValue(today);
studentNames.getCell(j, studentNamesLastColumn).offset(0, 1).setValue(dailyTime);
}
}
}
}