Google Script - Execute only if cell contains a certain string of text - google-apps-script

Completely new to using the AppsScript in Google but I came across a script that would allow GoogleSheets to count cells if coloured, and it automatically updates thanks to that last bit of code that sets a random value that essentially triggers a recalculation:
// Unsed third argument
function countColoredCells(countRange,colorRef,unUsed) {
var activeRg = SpreadsheetApp.getActiveRange();
var activeSht = SpreadsheetApp.getActiveSheet();
var activeformula = activeRg.getFormula();
// The regex matches the parentheses as an array, gets the arguments as a string,
// then splits the arguments on the comma into another array
var arrayOfArguments = activeformula.match(/\((.*)\)/).pop().trim().split(',');
// Get the first argument which is the range
var countRangeAddress = arrayOfArguments[0];
// Get the second argument, which is the reference color
var colorRefAddress = arrayOfArguments[1];
var backGrounds = activeSht.getRange(countRangeAddress).getBackgrounds();
var BackGround = activeSht.getRange(colorRefAddress).getBackground();
var countCells = 0;
for (var i = 0; i < backGrounds.length; i++)
for (var k = 0; k < backGrounds[i].length; k++)
if ( backGrounds[i][k] == BackGround )
countCells = countCells + 1;
return countCells;
};
// If Cell A1 has ColouredCells, Writes a random number to B1
function onEdit(e) {
if SpreadsheetApp.getActiveSheet().getRange('A1')="ColouredCells" {
SpreadsheetApp.getActiveSheet().getRange('B1').setValue(Math.random());}
}
Specifically I'm looking to edit the last part of the code so that this script only runs on sheets which I'd like it to (instead of randomly changing my B1 cell on all sheets I touch). As per the comments I'd like it to only run if cell A1 has the string "ColouredCells". Sorry newbie to all this so apologies if this is a really simple ask! Appreciate any help I can get on this one, I've tried googling quite a few different things but can't seem to to find the solution on this one!
This is the part I specifically need help with.
// If Cell A1 has ColouredCells, Writes a random number to B1
function onEdit(e) {
if SpreadsheetApp.getActiveSheet().getRange('A1')="ColouredCells" {
SpreadsheetApp.getActiveSheet().getRange('B1').setValue(Math.random());}
}
Thank you!

To run the code for specific sheets only you have to first get the name of the sheet and with an if-statement run the code as you wish
function onEdit(e){
// code
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
if ("Sheet 1" === sheet.getName() || "Sheet 2" === sheet.getName()) {
// Run your code
}
}

Related

Get notes from a cell and insert into another cell

I have a range of cells in Google Sheets, some of them have notes attached.
If there's a note attached to a cell, I need to put the note in a separate cell, and put the location of that note in another cell.
I found this script elsewhere:
function getNote(cell)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getRange(cell)
return range.getNote();
}
but when I try to use it I get an error "Exception: Range not found (line 12)."
But this script only gets me halfway there as it only gets the note and puts it in a cell. I also need to know what cell the note came from.
Any help is greatly appreciated.
In the script above the function getNote() expects the paramter cell
If you just run the script without calling getNote() from another function / an environment where it gets a values for cell assigned, the script will fail wiht the error you obtained.
Indeed, this script doe snot not meet your needs. What you probably want is to screen all your cells for the one that have notes.
What you need to decide is into which cells you want to put the note and the cell notation.
Below is a sample that you need to adapt for your needs:
function getNotes() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
//if you have only one sheet in the spreadsheet, otherwise use ss.getSheetByName(name);
var sheet = ss.getActiveSheet();
var range = sheet.getDataRange();
var results = range.getNotes();
for (var i = 0; i < results.length; i++) {
for (var j = 0; j < results[0].length; j++) {
//if a not empty note was found:
if(results[i][j]){
var note = results[i][j];
var cell = range.getCell(i+1, j+1);
var notation = cell.getA1Notation();
//adjust the offset as function of the column / row where you want to output the results
cell.offset(0, 1).setValue(note);
cell.offset(0, 2).setValue(notation);
}
}
}
}
Important references:
getNotes()
getA1Notation()
getDataRange()
getCell(row, column)
offset(rowOffset, columnOffset)

Copy active cell to other cells containing string

In Google Sheets I'm trying to create a script that will take the value from the active cell and paste that value to any cell in Column B containing the string "HR". Any ideas?
This isn't too bad; you just have to wrap your head around a few concepts from Apps Script and Javascript to make it efficient. But first let's start with the naive approach!
function firstTry() {
var activeSheet = SpreadsheetApp.getActiveSheet(); // whatever is open
var activeCell = SpreadsheetApp.getCurrentCell(); // this is a single-cell range
var activeCellValue = activeCell.getValue(); // could be a string, number, etc
// Now let's look in column B for stuff to change
for (var i = 1; i <= activeSheet.getLastRow(); i++) {
var cell = activeSheet.getRange("B" + i);
var val = cell.getValue();
var valStr = String(val); // We could have gotten a number
if (valStr.indexOf("HR") != -1) {
cell.setValue(activeCellValue);
}
}
}
This will probably work, but isn't too efficient: each call to getValue() or setValue() takes some time. It'd be better to just get all the values at once, and then paste back a modified Column B when we're satisfied:
function improvement() {
var activeSheet = SpreadsheetApp.getActiveSheet(); // whatever is open
var activeCell = SpreadsheetApp.getCurrentCell(); // this is a single-cell range
var activeCellValue = activeCell.getValue(); // could be a string, number, etc
// Now let's look in column B for stuff to change
var rowsWithData = activeSheet.getLastRow() - 1;
var colBRange = activeSheet.getRange(1, // start on row 1
2, // start on column 2
rowsWithData, // this many rows
1); // just one column
// Let's get the data as an array of arrays. JS arrays are 0-based, btw
var colBData = colBRange.getValues();
for (var i = 0; i < colBData.length; i++) {
var val = colBData[i][0]; // row i, first column
var valStr = String(val); // We might have gotten a number
if (valStr.indexOf("HR") != -1) {
colBData[i][0] = activeCellValue; // modify copied data
}
}
// Lastly, write column B back out
colBRange.setValues(colBData);
}
You could go further with a fancy filter function instead of looping over the data explicitly, but that starts to get less clear.
Caveats as the OP points out in comments below, blindly calling setValues like this will pave over any formulas you have. This would have been no big deal, except that this includes hyperlinks. You could get really involved by calling getFormulas in parallel with getValues and then decide whether to call setValue or setFormula depending on the original contents of each cell.

Google Apps Script - set formula of active cell depending on value of another cell

I’m writing a custom function in Google Apps Script that, if a certain other cell contains a number, uses the value of that cell and several other cells to calculate a result. Otherwise, if that certain other cell does not contain a number, the function should just return an empty string so that the active cell appears blank.
I was able to come up with a working function to do this, but in order to protect sensitive information, I’m not going to copy it here. Instead, here’s an example function that accomplishes the same thing:
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var rowNum = sheet.getActiveCell().getRow();
var rowVals = sheet.getRange(rowNum, 1, 1, 15).getValues();
var fVal = rowVals[0][5];
if (fVal == "" || isNaN(fVal)) {
return ""; //leave cell blank if column F doesn't contain a number
}
var aVal = rowVals[0][0];
var bVal = rowVals[0][1];
var cVal = rowVals[0][2];
var gVal = rowVals[0][6];
return ((gVal * fVal) + aVal + bVal + cVal);
}
However, in an effort to speed it up (and also some other reasons that would be complicated to try to explain here, so you'll have to just trust me), I want to have the custom function set the value of the cell to be a formula instead of doing the calculating itself. It doesn’t work to just put the formula in the cell in the first place because then it still calculates/shows a result even if column F doesn’t contain a number.
Here’s what I’ve tried so far:
function myFunction2() {
var sheet = SpreadsheetApp.getActiveSheet();
var rowNum = sheet.getActiveCell().getRow();
var fVal = sheet.getRange(rowNum, 6).getValue();
if (fVal == "" || isNaN(fVal)) {
return ""; //leave cell blank if column F doesn't contain a number
}
var formula = '=SUM((G2*F2)+A2+B2+C2)';
return formula;
}
^This just makes the cell display the string “=SUM((G2*F2)+A2+B2+C2)”.
So I then tried using setFormula on the active cell:
function myFunction3() {
var sheet = SpreadsheetApp.getActiveSheet();
var cell = sheet.getActiveCell();
var rowNum = cell.getRow();
var fVal = sheet.getRange(rowNum, 6).getValue();
if (fVal == "" || isNaN(fVal)) {
return ""; //leave cell blank if column F doesn't contain a number
}
cell.setFormula('=SUM((G2*F2)+A2+B2+C2)');
}
^which, when I called the function in a cell, returned an error saying “You do not have permission to call setFormula”. The same thing happened when I tried getting the a1 notation of the active cell and then using getRange(a1Notation).setFormula('=SUM((G2*F2)+A2+B2+C2)') instead of calling setFormula directly on the active cell.
Anybody know if there's a way around that permission error? or have any other ideas for how to accomplish this?
The permission error is because of restrictions on what user defined functions can do. You can, however, do this with onEdit like this:
function onEdit(e) {
var ss=SpreadsheetApp.getActiveSpreadsheet()
var s=ss.getActiveSheet();
var col = e.range.getColumn();
var rowNum = e.range.getRow();
if(col==6){
var fVal = s.getRange(rowNum,col,1, 1).getValue()
}
if (fVal == "" || isNaN(fVal)) {
return
}
else{
s.getRange(rowNum,col,1, 1).setFormula('=SUM((G2*F2)+A2+B2+C2)');
}}
I actually ended up figuring out a way to accomplish what I wanted using the built-in IF function, so that's what I did.

Google sheet script, times out. Need a new way or flip it upside down

noob here, so the script works great if there are less than 800 rows on a sheet, however this time I have a sheet of almost 1500 rows and the script times out.
Basically it is a quick way to get a quote. (quick here means 5-6mins, not an issue) It hides columns with calculations, hides columns with sensitive information and rows where there was no value in column H.
What I want to know is, if I can do the same with a different code, or if someone knows how to make getRange().getValue(); start at the bottom of the sheet, then I could have two scripts starting one after the other to finish the sheet and produce a printable quote.
Any help is greatly appreciated.
Many Thanks
here is the script:
function Quote()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName("Quote"); `
var datarange = s.hideColumns(6);
var datarange = s.hideColumns(9);
var datarange = s.hideColumns(10);
var datarange = s.hideColumns(12);
var datarange = s.hideColumns(13);
var datarange = s.hideColumns(14);
var lastRow = s.getLastRow();
for( i=1 ; i<=lastRow ; i++) {
var status = s.getRange("H"+i).getValue();
if (status == "") {
s.hideRows(i);
}
}
}
Your problem is row:
s.getRange("H"+i).getValue()
This code takes data from spreadsheet, this is very slow process when you use it inside loop. You may use this conctruction:
var data = s.getDataRange().getValues();
for (var i=0; i < data.length; i++) {
var status = data[i][7]; // takes column H
// other code goes here...
}
This way you read data from the spreadsheet only once using getDataRange(), and convert it into array with getValues(). Then loop should work way more faster.
When hiding rows, remember to add 1 because array starts from 0, heres code for hiding rows inside loop:
if (status == "") {
s.hideRows(i + 1); // adding 1
}

How to replace text in Google Spreadsheet using App Scripts?

I tried to get the value of a range and than remove all points from the cell.
var FILE = SpreadsheetApp.openById("xyz");
var CONTENT = FILE.getSheetByName("Sheet1");
var A1 = CONTENT.getRange("I17").getValue();
A1. replace(".", "");
It gives me that can't find the replace function. Is there any function in Google Apps Script that allows me to replace the string?
If this is an exact copy of your script then you have a space in-between A1. and replace but I assume it is not.
#SergeInsas is right the content needs to be a string for the replace() function to work, so if your trying to replace the . in a decimal number then you can use the toString() method first like below.
var FILE = SpreadsheetApp.openById("xyz");
var CONTENT = FILE.getSheetByName("Sheet1");
var A1 = CONTENT.getRange("I17").getValue();
var A1String = A1.toString().replace(".", "");
Or you can multiply the number to get rid of the decimal but this will depend on how many decimal places you have :)
There is a more powerful, and simpler, method available: TextFinder.
The accepted answer to this question requires an additional step to post the replaced string back to the cell.
The TextFinder method does not need you to write the data back to the cell.
And if you want to search multiple cells, then this method saves you the iterations.
var FILE = SpreadsheetApp.openById("xyz");
var CONTENT = FILE.getSheetByName("Sheet1");
var A1 = CONTENT.getRange("I17");
A1.createTextFinder(".").replaceAllWith("");
I haven't tested it on a large data set but I suspect this would be quite quick.
Edit: I wrote a short tutorial on this.
For some reason, this solution doesn't work for me. Here is my whole code that should replace the '+' symbol with 'nothing'
// I need to replace more occurrences of different strings, so this is just an example..
var ui = SpreadsheetApp.getUi();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getRange("G5:G7").getValues();
// this is a loop, to go through multiple cells that may contain the text, that needs to be replaced.
for (var i = 0 ; i<range.length ; i++) {
var le = range.length;
var stri = range[i].toString().replace("+", "");
Logger.log(stri);
}
var msg = ui.alert("Replaced?");
return msg;
Hope this help you
function removeAccents() {
var spreadsheet = SpreadsheetApp.getActive();
var range = spreadsheet.getRange("F3:F");
var data = range.getValues();
for (var row = 0; row < data.length; row++) {
for (var col = 0; col < data[row].length; col++) {
data[row][col] = (data[row][col]).toString().replace(/é/g, 'e');
data[row][col] = (data[row][col]).toString().replace(/ã/g, 'a');
}
}
range.setValues(data);
};
Sharing a very helpful solution from Bannager Bong on this Google Docs Editor Help Forum thread. Made a slight modification to the function so that it accepts arguments for the find, replace values and then added a range argument so that the function can target a specific region. Even so, this method is extremely slow (my sheets have 5k rows).
function Cleanup12m() {
var spreadsheet = SpreadsheetApp.getActive();
//fandr(",", "");
//fandr("\"","");
fandr("�","",spreadsheet.getRange('BA:BA')); //uses specific range
};
function fandr(find, repl) {
var r=SpreadsheetApp.getActiveSheet().getDataRange();
var rws=r.getNumRows();
var cls=r.getNumColumns();
var i,j,a,find,repl;
//find="abc";
//repl="xyz";
for (i=1;i<=rws;i++) {
for (j=1;j<=cls;j++) {
a=r.getCell(i, j).getValue();
if (r.getCell(i,j).getFormula()) {continue;}
//if (a==find) { r.getCell(i, j).setValue(repl);}
try {
a=a.replace(find,repl);
r.getCell(i, j).setValue(a);
}
catch (err) {continue;}
}
}
};
//Revised to apply to a selected range
function fandr(find, repl, range) {
var r= range;//SpreadsheetApp.getActiveSheet().getDataRange();
var rws=r.getNumRows();
var cls=r.getNumColumns();
var i,j,a,find,repl;
//find="abc";
//repl="xyz";
for (i=1;i<=rws;i++) {
for (j=1;j<=cls;j++) {
a=r.getCell(i, j).getValue();
if (r.getCell(i,j).getFormula()) {continue;}
//if (a==find) { r.getCell(i, j).setValue(repl);}
try {
a=a.replace(find,repl);
r.getCell(i, j).setValue(a);
}
catch (err) {continue;}
}
}
};