so I have a new problem.
So far my script here can loop a sheet and find the text "Bank", it will set the background color to red and it will take the value from another cell as marked and log it. Once it have done that once it will crash, so the loop will break, I have no idea why?
function sortBank() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var rowsDeleted = 0
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
if (row[8].indexOf('Bank') > -1) { /** Set the Job prefix **/
sheet.getRange(parseInt(i)+1,9).setBackgroundColor("#f44336");
var values = sheet.getRange(parseInt(i),2).getValues();
Logger.log(values[0][0]);
}
}
};
You may look at similar questions:
Google Script - Internal Error after 15 seconds
Google script - Exceeded maximum execution time , help optimize
Google sheet script, times out. Need a new way or flip it upside down
Basic solution is to use getValues() one time and then loop values in 2d array:
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var data = rows.getValues();
for (var i = 0; i < numRows; i++)
{
var j = SomeValue; // column number - 1
var row = data[i]; // row from origonal data range
var value = row[j]; // value from data
// some other code...
}
See more info about your problem here:
Your scripts will run faster if you can find ways to minimize the
calls the scripts make to those services.
Related
I need to refresh all of my custom formulas via a script in Google Sheets but this seems to take forever (like, 30 seconds for 100 cells). There will potentially be thousands of cells with my custom formula so I must come up with a better way. I have:
function refresher(){
var sheet = SpreadsheetApp.getActiveSheet();
var selection = sheet.getDataRange();
var columns = selection.getNumColumns();
var rows = selection.getNumRows();
for (var column=1; column <= columns; column++){
for (var row=1; row <= rows; row++){
var cell=selection.getCell(row,column);
var formula = cell.getFormula();
if (formula.startsWith("=myfunc(")){
cell.setFormula(formula.replace("=myfunc(", "?myfunc("));
}
}
}
SpreadsheetApp.flush();
for (var column=1; column <= columns; column++){
for (var row=1; row <= rows; row++){
var cell=selection.getCell(row,column);
var formula = cell.getFormula();
if (formula.startsWith("=?myfunc(")){
cell.setFormula(formula.replace("=?myfunc(", "=myfunc("));
}
}
}
}
In order to achieve your goal, how about using TextFinder? In this case, I think that the process cost might be able to be reduced. From your script, when TextFinder is used for your situation, it becomes as follows.
Sample script:
function refresher() {
const sheet = SpreadsheetApp.getActiveSheet();
const formula = "=myfunc";
const tempFormula = "=sampleFormula";
sheet.createTextFinder("^\\" + formula).matchFormulaText(true).useRegularExpression(true).replaceAllWith(tempFormula);
sheet.createTextFinder("^\\" + tempFormula).matchFormulaText(true).useRegularExpression(true).replaceAllWith(formula);
}
Reference:
Class TextFinder
I want to remove duplicates across 2 different sheets.
I have my active sheet, and I want to remove duplicates that already exist in my sheet "Blacklist". I want to run this process for both Column A and Column B (or simply for any values across the entire sheets). When a duplicate is found, I want to leave the row in tact but replace the value with '' (e.g. an empty cell).
I have a working version I mangled together, but only for the active sheet.
N.B. it's the findDuplicate function that I use, the removeDuplicate function I left there not to mess anything up :)
// this is a Google Apps Script project
function onOpen() {
var spreadsheet = SpreadsheetApp.getActive();
var menuItems = [
{ name: 'Find duplicates...', functionName: 'findDuplicate' },
{ name: 'Remove duplicates...', functionName: 'removeDuplicate' }
];
spreadsheet.addMenu('Duplicates', menuItems);
}
function removeDuplicate() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveRange();
var data = range.getValues();
var rowNum = range.getRow();
var columnNum = range.getColumn();
var columnLength = data[0].length;
var uniqueData = [];
var duplicateData = [];
// iterate through each 'row' of the selected range
// x is
// y is
var x = 0;
var y = data.length;
// when row is
while (x < y) {
var row = data[x];
var duplicate = false;
// iterate through the uniqueData array to see if 'row' already exists
for (var j = 0; j < uniqueData.length; j++) {
if (row.join() == uniqueData[j].join()) {
// if there is a duplicate, delete the 'row' from the sheet and add it to the duplicateData array
duplicate = true;
var duplicateRange = sheet.getRange(
rowNum + x,
columnNum,
1,
columnLength
);
duplicateRange.deleteCells(SpreadsheetApp.Dimension.ROWS);
duplicateData.push(row);
// rows shift up by one when duplicate is deleted
// in effect, it skips a line
// so we need to decrement x to stay in the same line
x--;
y--;
range = sheet.getActiveRange();
data = range.getValues();
// return;
}
}
// if there are no duplicates, add 'row' to the uniqueData array
if (!duplicate) {
uniqueData.push(row);
}
x++;
}
}
function findDuplicate() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveRange();
var data = range.getValues();
var rowNum = range.getRow();
var columnNum = range.getColumn();
var columnLength = data[0].length;
var uniqueData = [];
// iterate through each 'row' of the selected range
for (var i = 0; i < data.length; i++) {
var row = data[i];
var duplicate = false;
// iterate through the uniqueData array to see if 'row' already exists
for (var j = 0; j < uniqueData.length; j++) {
if (row.join() == uniqueData[j].join()) {
// if there is a duplicate, highlight the 'row' from the sheet
duplicate = true;
var duplicateRange = sheet.getRange(
rowNum + i,
columnNum,
1,
columnLength
);
duplicateRange.setValue('');
}
}
// if there are no duplicates, add 'row' to the uniqueData array
if (!duplicate) {
uniqueData.push(row);
}
}
}
Thanks so much for your help! I've been at this for a few hours and figured I should just ask the experts for advice :)
The first lines of both your removeDuplicate and findDuplicate function seems indeed to indicate that you refer to the active spreadsheet / sheet / range
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveRange();
var data = range.getValues();
If you want to be able to use the same function for a given spreadsheet / sheet / range which is not the active one, you will need to use other functions than the getActiveXXX().
For example, to get the sheet named "Blacklist", you should use
sheet = spreadsheet.getSheetByName("Blacklist")
(see also https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getsheetbynamename)
If you want to access a specific range which differs from the active range, you should use the getRange method (see also https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getrangea1notation)
Note that getRange method can be used in different ways, e.g.
getRange("A1:D4"), getRange(1, 1, 3, 3) (the parameters being respectively startRow, startColumn, numRows,numColumns)
Additionally, if you don't want to hardcode the last line of your 2 columns, you will most probably need this function to find the last line in the code :
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getlastrow
(there is also an example there showing how to use getRange() in combination with getLastRow()).
I hope this will help you going further.
Please note that I didn't check the rest of your code and just assumed that your deduplication logic works fine as you mentioned it in your commment.
Good luck !
First off, I am not a coder at all, just a teacher who's handy at googling things to make life easier. In my attendance book, I bold the times a student comes in tardy (they get a 1 if present and a 0 if absent in order to calculate attendance rate).
I found an awesome script that allows me to count the number of bold items in a range. However, the range is set and I can't specify a new range within google sheets for each student as is necessary.
I tried changing it to "function countColoredCells(countRange)" but it doesn't work as I assume there is something else I have to do within the rest of the script.
I literally have little to no coding knowledge and would really appreciate any help to solve this!
function countboldcells() {
var book = SpreadsheetApp.getActiveSpreadsheet();
var sheet = book.getActiveSheet();
var range_input = sheet.getRange("C3:S3");
var range_output = sheet.getRange("N3");
var cell_styles = range_input.getFontWeights();
var count = 0;
for(var r = 0; r < cell_styles.length; r++) {
for(var c = 0; c < cell_styles[0].length; c++) {
if(cell_styles[r][c] === "bold") {
count = count + 1;
}
}
}
range_output.setValue(count);
}
range_input in the existing script is hard-coded. This is unsatisfactory because it doesn't permit analysis on a student-by-student basis. To fix this, you need to loop through the data for each student, and do 'countbold' for each student.
Let's assume that "C3:S3" is the range for a single student. Let's also assume that the data for other students is contained in each subsequent row, and that there are two header rows.
To do:
Work out the number of rows of student data - refer variable ALast.
Get the data for all students in one go. Why? Because this is more efficient than getting the data one row at a time - refer range_input discussed below.
Loop through each row of the data (i.e. loop by student - using a "for" loop).
Count the bold cells and update the results for each student - using most of your existing code;
Note:
The destination range (range_output) is calculated for each row, using getRange (row,column). This could have been done by saving values to an array, and updating all the values in a single process, but I though it was better to retain the approach the OP had already taken, and not over-complicate matters. If there are a LOT of students AND the code is taking too long to run, then updating the counts by array would be more efficient.
The input range (range_input) is defined using getRange(row, column, numRows, numColumns).
row = 3, the first row of data
column = 3, Column C
numRows = a calculated value (ALast minus two header rows)
numColumns = Columns C to S inclusive = 17 (assigned to a variable).
function so54260768() {
// Setup spreadsheet and target sheet
var book = SpreadsheetApp.getActiveSpreadsheet();
var sheet = book.getActiveSheet();
// get the number of students in Column A
var Avals = book.getRange("A1:A").getValues(); // assuming rows one and two are headers
var Alast = Avals.filter(String).length;
//Logger.log("DEBUG: The last row on A = " + Alast);// DEBUG
// number of columns in the data range
var NumberofColumns = 17;
// get the data for all students
var range_input = sheet.getRange(3, 3, Alast - 2, NumberofColumns); // the first two rows are headers
var cell_styles = range_input.getFontWeights();
// start loop though each row - one row per student
for (z = 0; z < Alast - 2; z++) {
// set the bold counter to zero
var count = 0;
//loop through the cells in this row; count the cells that are bold
for (var i = 0; i < NumberofColumns; i++) {
if (cell_styles[z][i] === "bold") {
count = count + 1;
}
}
//Logger.log("DEBUG: row="+(z+3)+", count="+count);//DEBUG
var range_output = sheet.getRange(z + 3, 14).setValue(count); //. row, column
}
}
So I am trying to pull the value that is in the latest line of google sheets. Here is the part of the script that I am having trouble with. This script is being written within google scripts.
When this script runs the confirmation email it sends has the value of var cell as undefined. 0
function formSubmitReply(e) {
var userEmail = e.values[3];
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
// Set the status of the new ticket to 'New'.
// Column F is the Status column
sheet.getRange(lastRow, getColIndexByName("Status")).setValue("New");
var supportEmail;
// Calculate how many other 'New' tickets are ahead of this one
var numNew = 0;
for (var i = 2; i < lastRow; i++) {
if (sheet.getRange(i, getColIndexByName("Status")).getValue() == "New") {
numNew++;
}
}
var range = sheet.getRange("B2:B100");
var values = SpreadsheetApp.getActiveSheet().getDataRange().getValues()
Logger.log(values[0][0]);
for(n=0;n<values[0].length;++n){
var cell = values[n][1]
}
The problem is here:
for(n=0;n<values[0].length;++n){
var cell = values[n][1]
}
values[0].length is the number of columns while values[n][1] goes through rows.
if you need a loop to go over the rows use this:
for(n=0;n<values.length;++n){
var cell = values[n][1]
}
Otherwise if you have 10 columns and 50 rows, your loop will only go up to ten as values[0].length is 10 and you will pull data from:
A1
A2
A3
A4
and etc until
A10
and then loop will stop.
I'm using Google Apps Script to create a Google Sheet populated with information from my emails. In column F, I want to create the same formula in all of the rows that will split the delimited values in Column E.
var LastColumn = sheets[1].getLastColumn();
sheets[1].insertColumnAfter(LastColumn);
var LastRow = sheets[1].getLastRow();
for (var a = 1; a < LastRow; a++){
var cell = sheets[1].getRange('F'+a);
var place = "=SPLIT(E"+a+",',')";
cell.setValue(place);
};
While testing, the dynamic range never writes the set value formula. I've changed the formula to a hard-coded value, but it still does not write to the sheet.
Any thoughts?
Thanks,
Use setFormula:
function myTest() {
var sheet = SpreadsheetApp.getActiveSpreadsheet()
var LastColumn = sheet.getLastColumn();
sheet.insertColumnAfter(LastColumn);
var LastRow = sheet.getLastRow();
for (var a = 1; a < LastRow; a++){
var cell = sheet.getRange('F'+a);
var place = '=SPLIT(E'+a+', ",")';
cell.setFormula(place);
}
}