I have a button that I want to click, which will scroll me to a certain position. I've done this in order to get me to row 100:
function ScrollMe(){
var file = SpreadsheetApp.getActiveSpreadsheet();
var sheet = file.getActiveSheet();
var row = 100;
file.setActiveCell(sheet.getRange(row,1));
}
What I want to do if a find a list of all cells that are in column 'B' that contain (REGEX=>"Version: [0-9]?[0-9]?[0-9][.]?[0-9]?[0-9]? [a-zA-Z]+"), and then go to the last value that this is like. So basically, go to the last cell in column 'B' that starts with "Version: " and then has a single, double, or triple-digit number, a decimal point, and then two numbers after, and then any amounts of letter text after the fact. I want it to look like this:
function ScrollMe(){
var file = SpreadsheetApp.getActiveSpreadsheet();
var sheet = file.getActiveSheet();
//C# lambda
var row = FindAll(a=> a.COLUMN == 'B' && a.VALUE.RegexMatch("Version: [0-9]?[0-9]?[0-9][.]?[0-9]?[0-9]? [a-zA-Z]+"));
file.setActiveCell(sheet.getRange(row,1));
}
I assume that you expect the script to find the last cell in the column B that match your regex. If that is the case, you can use this code:
function ScrollMe() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = sheet.getRange("B:B").getValues();
var regex = new RegExp(
'Version: [0-9]?[0-9]?[0-9][.]?[0-9]?[0-9]? [a-zA-Z]+');
for (var i = 0; i < data.length; i++) {
if (regex.test(data[i][0])) {
var lastMatch = i;
}
}
sheet.setActiveRange(sheet.getRange(lastMatch + 1, 2));
}
The previous code used your approach. It will first read the full column B, and after that will iterate to find the last cell that match the regex; and when it finds the cell, it will select it. Please, ask me if you have any doubts about the function.
Related
So this question seems to be beaten to death on the boards, but with all my reading and googling, I just can't figure out what I'm doing wrong.
I'm trying to adapt the code from this link
How to loop a google spreadsheet column values and set result in column B?
Below is what I've adapted it to
function EquationIterationTest(){
var s = SpreadsheetApp.getActiveSpreadsheet();
var sht = s.getSheetByName('Heath, OH')
var drng = sht.getDataRange();
var rng = sht.getRange(13, 2, 111, 1)
//.getRange(13, 2, drng.getLastRow()-1, drng.getLastColumn())
var rngA = rng.getValues();//Array of input values
Logger.log(rngA);
for(var i = 0; i < rngA.length; i++) {
if(rngA[i][0] === 'subtotal'){
rng.offset(0,3).setFormula('=iferror(sum(filter(Invoices!$E:$E,Invoices!$F:$F=$B14,Invoices!$A:$A=$C$2)))');
}
else{
rng.offset(0,3).setValue('Dumb');
}
}
}
When I run this, rngA does get the first column of values (which in this instance starts at B13) however, it will not input the formula in the third column of values. Instead it moves right through the first if statement and executes the else statement. The only thing I can think is there's something wrong either with my if statement or my array.
I tried setting if(rngA[i][0] === 'subtotal') to if(rngA[i][1] === 'subtotal'), but that still returned "dumb" on every line.
Any help would be appreciated so I can stop being "dumb"!
Here's the link to my sheet.
https://docs.google.com/spreadsheets/d/1cDkwWThXDTssH89gJX7W1zKzsW86oLXO-FPfAIJvc-g/edit?usp=sharing
Thanks
The problem is not in your if condition, although if you use three equal signs you are making a strict comparison, so subtotal in your case should start with capital letter.
That said, your problem is happening when you assign a value or formula to rng.offset(0,3), because the result of that expression is a range with the same size as rng but offset 3 columns to the right. You can verify this by using: Logger.log(rng.offset(0,3).getA1Notation());, thus whenever you assign a value or formula there you are assigning it to the whole offset rng. Not what you want right?
You should use offset() from a single cell in your case, not a whole range.
Your function could be simplified to something like the following:
function EquationIterationTest(){
var s = SpreadsheetApp.getActiveSpreadsheet();
var sht = s.getSheetByName('Heath, OH')
var rng = sht.getRange(13, 2, 111, 1)
for(var i = 1; i <= rng.getNumRows(); i++) {
var cell = rng.getCell(i,1);
if(cell.getValue() === 'Subtotal'){
cell.offset(0,3).setFormula(
'=iferror(sum(filter(Invoices!$E:$E,Invoices!$F:$F=$B14,Invoices!$A:$A=$C$2)))'
);
}
else{
cell.offset(0,3).setValue('Dummy');
}
}
}
So I want to make sure I put this here because while the answer above was previously correct, I figured out a much faster way to do it using arrays. This function checks against the data range for a value (in this case "Subtotal") and then appends the equation to rows that that do not contain that value. It is easy to make it compare against the value though by changing the operator from != to ==.
function NewIterationTest(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activeSheet = ss.getActiveSheet();
var data = activeSheet.getRange(13, 2, 112, 1).getValues();
for (var i = 0; i < data.length; i++){
var rowData = data[i];
var checkData = data;
var row = checkData[i];
var colB = row[0];
if(colB != 'Subtotal'{
activeSheet.getRange(13 + i, 5).setFormula('=iferror(sum(filter(Invoices!$E:$E,Invoices!$F:$F=$B14,Invoices!$A:$A=$C$2)))');
}
}
}
However, if anyone could tell me how to also eliminate compare against whether or not a the text is bold, that would be helpful. Not sure it can be done though since it's pulling against the array.
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.
I am trying to have the user input text like , 9188675309 in the input box and store that into searchstring variable.
Then it needs to scan the entire column of R which is formatted to look like (918) 867-5309
If the user puts in 5309 or 86753 can it also find the matches?
If any or all matches are found and then highlight the whole row,
function seekAndselect(){
var searchString = Browser.inputBox("Search For Phone Number?");
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
var cell = ss.getActiveCell();
cell.setBackground('#ffff55');// replace by cell.setBackground(null); to reset the color when "leaving" the cell
var activeR = cell.getRow()-1;
var activeC = cell.getColumn()-1;
var data = ss.getDataRange().getValues();
var step = 0
for(var r=0;r<data.length;++r){
for(var c=0;c<data[0].length;++c){
step++
Logger.log(step+' -- '+searchString+' = '+data[r][c]);
if(data[r][c]==''||step==1){ continue };
if(searchString.toString().toLowerCase()==data[r][c].toString().toLowerCase()){
ss.setActiveSelection(r+1,c+1);
return;
}
}
}
}
Will there be more than one match? It was a bit unclear. If there could be more than one match, then loop through the R column and match() the input/values. This way also allows for partial matches.
function seekAndselect(){
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
// User input
var searchString = Browser.inputBox("Search For Phone Number?");
// Remove non numbers
var clean_search = searchString.replace(/[^0-9]/g, "");
// Get the R column values
var r_col = ss.getRange(1, 18, ss.getLastRow()).getValues();
// Search each row in col R for a match and highlight the row
r_col.forEach(function (e, i) {
// Remove non numbers from data before matching
if (e[0].replace(/[^0-9]/g, "").match(clean_search)) {
var found_range = ss.getRange((i + 1), 1, 1, ss.getLastColumn())
// Highlights the row from col A to R
found_range.setBackground('#ffff55')
// Sets the active selection to the last matched row from col A to R
ss.setActiveSelection(found_range)
}
});
}
I wasnt sure what you wanted with the setActiveSelection(), if there are multiple matches it will only select the last one. Also, if there are a lot of matches, this could take a long time to run, possibly even time-out. If that happens, a batchUpdate() would be needed
Let's say I have a lot of columns and one of them contains "impressions" string (on row 3). What I need to do is to:
1) Find the cell with "impressions" string
2) Get column number or i.e. "D"
3) Based on what I got paste a formula into i.e. D2 cell which gets AVERAGE from a range D4:D*last*
I couldn't find it anywhere so I have to ask here without any "sample" code, since I have no idea on how to achieve what I want. (3rd one is easy but I need to get that "D" first)
There's no way to search in Google Apps Script. Below is a function that will accomplish the first 2 parts for you (by iterating over every cell in row 3 and looking for "impressions"):
function findColumnNumber() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1'); // insert name of sheet here
var range = sheet.getDataRange(); // get the range representing the whole sheet
var width = range.getWidth();
// search every cell in row 3 from A3 to the last column
for (var i = 1; i <= width; i++) {
var data = range.getCell(3,i)
if (data == "impressions") {
return(i); // return the column number if we find it
}
}
return(-1); // return -1 if it doesn't exist
}
Hopefully this will allow you to accomplish what you need to do!
The indexOf method allows one to search for strings:
function findColumnNumber() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet() //whatever tab the code is run on
var data = sheet.getDataRange().getValues();
var header_row_num = 1; // TODO: change this to whichever row has the headers.
var header = data[header_row_num -1] //Remember JavaScript, like most programming languages starts counting (is indexed) at 0. For the value of header_row_num to work with a zero-index counting language like JavaScript, you need to subtract 1
//define the string you want to search for
var searchString = "impressions";
//find that string in the header and add 1 (since indexes start at zero)
var colNum = header.indexOf(searchString) + 1;
return(colNum);
i have a spreadsheet that i keep track of tasks i need to do, once complete i enter a date in the last column. What i want is for that completed task to be moved to sheet 2.
At present i have sheet 1 named SUD_schedule and i want the completed row of data to be moved to sheet 2 named SUD_archive. I've looked through the forum posts already and i've tried a variation of scripts but so far no luck. The closest i have come is this script:
function onEdit() {
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();//Original sheet
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];//target sheet
// to act on only one sheet, check the sheet name here:
//If it si not first sheet, it will do nothing
if (sheet1.getSheetName() != "SUD_schedule") {
return;
}
//Get Row and column index of active cell.
var rowIndex = sheet1.getActiveRange().getRowIndex();
var colIndex = sheet1.getActiveRange().getColumnIndex();
//If the selected column is 10th and it is not a header row
if (colIndex == 16 && rowIndex > 1) {
//Get the data from the current row
var data = sheet1.getRange(rowIndex,1,1,9).getValues();
var lastRow2;
(sheet2.getLastRow()==0)?lastRow2=1:lastRow2=sheet2.getLastRow()+1;
//Copy the data to the lastRow+1th row in target sheet
sheet2.getRange(lastRow2,1,1,data[0].length).setValues(data);
}
}
Column P (16) is the task complete date, row 1 is frozen and contains column headers.
Can anybody help show where i'm going wrong.
Kind regards
Den
Your code is not generic and you are more complicating your objective. Below will work out your need.
function onEdit(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('SUD_schedule');
var sheet2 = ss.getSheetByName('SUD_archive');
var dateColumn = "16";
var array = []
var range = sheet1.getRange(1, 1, sheet1.getLastRow(), dateColumn);
for (var i = 2; i <= sheet1.getLastRow(); i++) //i iterates from 2 as you say R1 is header
{
if(isValidDate(range.getCell(i, dateColumn).getValue()) == true) //checking if any values on column16 is valid date
{
data = sheet1.getRange(i, 1, 1, dateColumn).getValues(); //Getting the range values of particular row where C16 is date
for (var j = 0; j < dateColumn; j++) //Adding the row in array
{
array.push(data[0][j]);
}
}
if(array.length > 0)
{
sheet2.appendRow(array); //Appending the row in sheet2
array = [];
sheet1.deleteRow(i); //deleting the row in sheet as you said you want to move, if you copy remove this and next line
i=i-1; //managing i value after deleting a row.
}
}
}
//Below function return true if the given String is date, else false
function isValidDate(d) {
if ( Object.prototype.toString.call(d) !== "[object Date]" )
return false;
return !isNaN(d.getTime());
}
I am not sure that the syntax you have as used below is entirely correct.
(sheet2.getLastRow()==0)?lastRow2=1:lastRow2=sheet2.getLastRow()+1;
sheet2.getRange(lastRow2,1,1,data[0].length).setValues(data);
What I know will work for certain is if you omit the variable lastRow2 all together and use this instead.
sheet2.getRange(getLastRow+1,1,1,data[0].length).setValues(data);
To complement Joachin's answer, here is how you can adapt that code if you don't have the date in the last row. In the below shown part of the code replace Lastcolumnumber with your last column.
//Getting the range values of particular row where C16 is date
data = sheet1.getRange(i, 1, 1, LASTCOLUMNNUMBER).getValues();
//Adding the row in array
for (var j = 0; j < LASTCOLUMNNUMBER; j++)