Google Sheets Script - Get first blank cell - javascript

I want to find the first blank cell in a range. This code works, except it seems to be linked to column B.
function selectFirstEmptyRow() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
sheet.setActiveSelection(sheet.getRange("D"+getFirstEmptyRow()))
}
function getFirstEmptyRow() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getDataRange();
var values = range.getValues();
var row = 26;
for (var row=26; row<values.length; row++) {
if (!values[row].join("")) break;
}
return (row+1);
}
When I run it, it selects the cell in D, however finds the blanks based on B.
Say we are working in rows 10-20. In column B there is stuff in 10-12. I run this (which should run on D as far as I can see), it will select D13 (because there is stuff in B10:B12. Even though nothing is in D, it should select D10.
UPDATE:::
So I made some changes you suggested.
function selectFirstEmptyRow() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
sheet.setActiveSelection(sheet.getRange("B"+getFirstEmptyRow()))
}
function getFirstEmptyRow() {
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
//getRange(row, column, numRows, numColumns)
var range = sheet.getRange(28, 2, lastRow - 28, 1);
var values = range.getValues();
var row = 28;
for (var i=0; i<values.length; i++) {
if (!values[i].join(" ")) break;
}
return (row+1);
}
It always selects row 29 (I assume because it's starting on 28 and I have return(row+1)?

This line:
var row = 26;
hard codes a starting number of 26. The loop count starts at 26:
for (var row=26; row<values.length; row++) {
If you want the loop to loop the number of times from a start row of 10, to the last row, then change that line to this:
for (var i=0; i<values.length; i++) {
Your code was actually defining the variable row twice. Once just above the for loop, and once inside of the for loop. The row variable shouldn't be used in the loop at all. The for loop needs it's own counter.
In this line of code:
for (var row=26; row<values.length; row++) {
The JavaScript Array join() Method returns a string from an array.
The getDataRange() method from this line of code:
var range = sheet.getDataRange();
Returns a two dimensional array of all the rows and columns in the sheet. The range starts in the upper left hand side of data in the sheet. If you want to exclude the columns up to and including column C, then use something different to get a different range.
So, I would change this line:
var range = sheet.getDataRange();
Maybe change the code to:
var lastRow = sheet.getLastRow();
//getRange(row, column, numRows, numColumns)
var range = sheet.getRange(10, 4, lastRow - 10, 1);
The above code will get a two dimensional array of data, starting in row 10, Column 4, and get 10 less rows than the total number of rows, and only get one column of data from column D (4th column)

Related

Google sheet script auto add new rows with copied formulas

I need to create function which adds rows with copied formulas from above rows. After the script is launched it should result in accurate number (set 5 i this code) of blank rows at the end of the sheet.
The code I managed to create counts what number of rows should be added but adds only one row with copied formulas at the end.
Please help me edit this code to multiple the result of the function by "rowstoadd" parameter.
Sheet image
function autoaddRows() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Harmonogram');
var range = sheet.getRange("B2:B").getValues();
var lastRowB = range.filter(String).length + 2;
var lastRowA = sheet.getLastRow();
var blanknrows = sheet.getLastRow() - lastRowB;
if (blanknrows < 5) {
let rowstoadd = 5 - blanknrows;
Browser.msgBox(rowstoadd);
let spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
let lastRowIndex = sheet.getLastRow();
let existingRange = getRowRange(sheet, lastRowIndex);
sheet.insertRowAfter(lastRowIndex);
let newRange = getRowRange(sheet, ++lastRowIndex);
existingRange.copyTo(newRange);
newRange.setFormulas(newRange.getFormulas());
newRange.clearNote();
}
function getRowRange(sheet, rowIndex) {
return sheet.getRange(rowIndex, 1, 1, sheet.getLastColumn());
}
}
I believe your goal is as follows.
You want to keep 5 new empty rows.
For example, when the last row of column "A" is 10 and the last row of column "B" is 9, you want to add 4 new rows.
Also, you want to put the formula to the column "B" of the inserted rows.
In this case, how about the following modified script?
Modified script:
function autoaddRows() {
var addRows = 5;
var sheet = SpreadsheetApp.getActive().getSheetByName('Harmonogram');
var range = sheet.getRange("B2:B").getValues();
var lastRowB = range.filter(String).length + 1;
var lastRow = sheet.getLastRow();
var blanknrows = lastRow - lastRowB;
var diff = addRows - blanknrows;
if (diff > 0) {
sheet.insertRowsAfter(lastRow, diff);
// var range = sheet.getRange("B" + lastRowB);
// range.copyTo(range.offset(1, 0, diff + 1), SpreadsheetApp.CopyPasteType.PASTE_FORMULA, false);
var numberOfCol = sheet.getLastColumn() - 1;
var range = sheet.getRange(lastRowB, 2, 1, numberOfCol);
range.copyTo(range.offset(1, 0, diff + 1, numberOfCol), SpreadsheetApp.CopyPasteType.PASTE_FORMULA, false);
range.clearNote();
}
}
In this modification, the difference between the last rows between column "A" and column "B" is retrieved. And the empty rows are inserted using the difference.
In your script, newRange.clearNote(); is used. So, I also add range.clearNote();. If you want to remove it, please remove it.
References:
insertRowsAfter(afterPosition, howMany)
copyTo(destination, copyPasteType, transposed)

hideColumns() number of columns parameter based on referenced column index Google Apps Script Google Sheets

I am trying to do hideColumns() in google sheets. The starting column is always fixed (column B) but the number of columns to hide is variable depending on a date function. Each cell in the header row has a date, and I want to hide all columns with a date before today's date. So I did a lookup and found the index number of the column with today's date and I want to simply reference that index minus 1 to come up with the number of columns to hide.
The below will work if I hard code a value for number of columns (e.g. 3) but it won't work if I reference i or even try to redefine i as a variable
function onOpen(e) {
// Get column index of yesterday's date
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getSheetByName('Daily Tracker');
var lastColumn = sheet.getLastColumn();
var data = sheet.getRange(1,2,1,lastColumn).getDisplayValues().flat(); //create an array of data from row 1
for (var i = 0; i <= data.length; i++) {
var dateToday = Utilities.formatDate(new Date(), "EST", "MM/dd/yyyy")
if (data[i] == dateToday)
{
return i;
break;
}
var hiddenColumnCount = i - 1;
// Number of columns to hide = index - 1 (e.g. 10/14/2020 = index of 8. I need to hide columns 10/6/2020 through 10/13/2020, which = 7 columns so i - 1)
// Hide all columns from column B through column with yesterday's date
sheet.hideColumns(2,hiddenColumnCount);
}
}
I don't know if i is unable to be referenced because maybe it's a string and not a number since I flattened the array from above. But I tried converting to a number using parseInt() but not sure if it worked or if it's the right thing. I've also tried defining var hiddenColumnCount = 0 above the for statement and then instead of return i I tried typing hiddenColumnCount = i - 1 but this didn't work either
I'm new to programming and try to look everything up on my own but I've hit a road block
I believe your goal as follows.
From Each cell in the header row has a date, and I want to hide all columns with a date before today's date. and I want to simply reference that index minus 1 to come up with the number of columns to hide., in your situation, when the date of today can be found, you want to hide the columns "B" to the left side of the column with the date.
Modifcation points:
In your case, it seems that the column number of the date of today can be retrieved. I think that this can be used for achieving your goal.
When the array of data is looped, please modify the condition of for loop from for (var i = 0; i <= data.length; i++) { to for (var i = 0; i < data.length; i++) {. When i <= data.length is used, the last loop is the index of out of array.
In your script, when the date of today is found, the script is finished.
When hideColumns is used, I think that in your case, this can be put to the out of for loop.
When above points are reflected to your script, it becomes as follows.
Modified script:
function onOpen(e) {
// Get column index of yesterday's date
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getSheetByName('Daily Tracker');
var lastColumn = sheet.getLastColumn();
var data = sheet.getRange(1,2,1,lastColumn).getDisplayValues().flat(); //create an array of data from row 1
// I modified below script.
var columnNumber = 0;
for (var i = 0; i < data.length; i++) {
var dateToday = Utilities.formatDate(new Date(), "EST", "MM/dd/yyyy");
if (data[i] == dateToday) {
columnNumber = i + 2;
}
}
sheet.hideColumns(2, columnNumber - 2);
// Here, columnNumber is the column number of the found date.
}
Note:
In this modification, it supposes that the date is the ascending order from the column "B". Please be careful this. When above modified script is not the result you expect, I think that it might be required to confirm the situation of Spreadsheet.
Reference:
hideColumns(columnIndex, numColumns)
Try this:
function onOpen(e) {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Daily Tracker');
var data = sheet.getRange(1,2,1,sh.getLastColumn()).getDisplayValues().flat();
for(var i = 0; i <= data.length; i++) {
if (data[i]==Utilities.formatDate(new Date(), "EST", "MM/dd/yyyy")){break;}else{sh.hideColumn(i+2);}
}
}

Google Apps Script Taking one random cell from each row

So I have a sheet as seen here.
https://docs.google.com/spreadsheets/d/1r2ogg1ldR0CFjOJ6mVObY-WRVipJx_X3pQ0yM2HYEog/edit?usp=sharing.
I am trying to write a script that goes through every row in range A1:D and chooses one random cell to put in the F column of the same row.
I am new to GAP so Im not sure how to write the exact script for that. This is what I got so far
function random() {
var sss = SpreadsheetApp.getActiveSpreadsheet();
var ss = sss.getSheetByName('Sheet1'); //the sheet that has the data
var range = ss.getRange('A1:D'); //the range I need
var data = range.getValues();
for(var i = ; i < data1.length; i++) { //at this point im just guessing based on online codes
= Math.floor(Math.random()*(j+1)); //method of randomization
ss.getRange('the i row of F column').setValue(data[i][1]); //choosing the row that is being used, selecting the first item of the shuffled array
};
}
You've been pretty close to the solution:
function random() {
var sss = SpreadsheetApp.getActiveSpreadsheet();
var ss = sss.getSheetByName('Sheet1'); //the sheet that has the data
var range = ss.getRange(1,1,ss.getLastRow(), 4); //the range you need: 4 columns on all row which are available
var data = range.getValues();
for(var i = 0; i < data.length; i++)
{
var j = Math.floor(Math.random()*(data[i].length)); //method of randomization
var element = data[i][j]; // The element which is randomizely choose
ss.getRange(i+1, 6).setValue(element);
}
}
I've made some modification on your orignal code:
getLastRow() get the position of the last row which is use. Otherwise, you collect a array the size of your entire sheet.
The Math.random() function is multiply by the size of a row, so you can have as much column as you want.

Average of Numbers in Column If Condition In Different Column Satisfied

I have a data range of which the first column are dates and the second column are numerical values. There are some rows where the second column value is blank and I do not want them to be counted.
I am trying to find the average of the second column values if they satisfy the criteria of being within 3 months ago from today (blank values should not be counted).
But I am stuck as I cannot even get the total correct. And I do not know how to proceed further to get the average.
The code belows seem to give me appended strings instead of summing up the numbers mathematically.
Can anyone help please?
Thanks in advance.
function average() {
// open spreadsheet
var spreadsheet = SpreadsheetApp.openById("spreadsheetID");
// set the named sheet as active
var sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheetByName("SheetName"));
// figure out what the last row is
var lastRow = spreadsheet.getSheetByName("Form responses 1").getLastRow();
// the rows are indexed starting at 1, and the first row is the headers, so start with row 2
var startRow = 2;
// get the data range
var responsesValues = spreadsheet.getSheetByName("Form responses 1").getRange("A1:Q" + lastRow).getValues();
// define the dates
var timeToday = new Date().getTime();
var dateToday = new Date().getDate();
var date = new Date();
var threeMonthsAgo = new Date(date.getFullYear(), date.getMonth() - 3, 0);
// grab column 1 (date of entry column) (second variable in getRange function below)
var dataRange = sheet.getRange(2,1,lastRow-startRow+1,1 );
var numRows = dataRange.getNumRows();
var dateOfEntryValues = dataRange.getValues();
// grab column 2 (values to be averaged)
range = sheet.getRange(2, 2, lastRow-startRow+1, 1);
var Values = range.getValues();
var warning_count = 0;
var sumValues = 0;
// Loop over the values
for (var i = 0; i <= numRows - 1; i++) {
var dateOfEntry = dateOfEntryValues[i][0];
if(dateOfEntry > threeMonthsAgo && dateOfEntry !== "") {
// if it's within 3 months ago, add the values.
sumValues += Values[i][0];
warning_count++;
}
}
There is a lot more simple version. Just put into the Destination Cell the formula
=ArrayFormula(AVERAGE(IF(DATEDIF(C1:C;TODAY();"D")<=90;E1:E)))
replacing C1:C with the column with dates and E1:E with the column with numbers.

script to create a link to a file in a spreadsheet creating error "Cannot Convert"

This script searches a spreadsheet and copies the link to the third column according to the value in the first column
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var searchMenuEntries = [ {name: "Search in all files", functionName: "search"}];
ss.addMenu("Search Document List", searchMenuEntries);
}
function lastValue(column) {
var lastRow = SpreadsheetApp.getActiveSheet().getMaxRows();
var values = SpreadsheetApp.getActiveSheet().getRange(column + "1:" + column + lastRow).getValues();
for (; values[lastRow - 1] == "" && lastRow > 0; lastRow--) {}
return values[lastRow - 1];
}
function search() {
// Get the active spreadsheet and the active sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
// Search the files in the user's Docs List for the search term
var startRow = 2; // First row of data to process
var numRows = lastValue("A"); // Number of rows to process
// Fetch the range of cells A2:B3
var dataRange = sheet.getRange(startRow, 1, numRows)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i in data) {
var files = DocsList.find(data[i].toString());
for (var j = 0; j < files.length; j++) {
if (files[j].getType() == "other"){
urlBase = "https://docs.google.com/fileview?id=";
sheet.getRange(i, 3, 1, 1).setValue(urlBase + files[j].getId());
}
}
}
}
However when I run it, I get the following error:
Cannot convert d59f868312238f16bd8534f61c01dd0695512d38 in (class)
The string above is the last row value in the column A which I use to retrieve the link (using DocsList.find)
Does anyone knows what is the problem with my code?
Thanks
You can use DriveApp instead.
var files = DriveApp.getFilesByName(data[i].toString());
while (files.hasNext()) {
var file = file.getNext();
if (file.getMimeType().indexOf("other") !== -1) {
Logger.log(file.getUrl());
}
}
When I ran your code, I got an error on this line:
var dataRange = sheet.getRange(startRow, 1, numRows);
You are using the wrong syntax for getRange(). I got an error msg that was similar to yours.
getRange can take two types of notation parameters, A1 notation or R1C1 notation.
A1 notation is a string, you have to put it into quotes:
var cell = sheet.getRange("A1");
R1C1 notation takes four settings. You have three. R1C1 notation is:
Start Row, Start Column, Number of Rows to Get, Number of Columns to Get
It's not explained in the getRange() documentation, but you can see an explanation in the getSheetValues() documentation here:
Google Documentation - getSheetValues
I refactor the whole program because it has some design flaws. And I managed to get rid of errors. However, after running for a while, the script will stop saying that it exceeded the time for running a script. I put the sleep function, as it was requested by Google, as many requests are made. However It is not able to finished the 370 rows that I have in my spreadsheet (it stops at 318)
Do you know if there is any way to improve the performance of my algorithm?
function search() {
// Get the active spreadsheet and the active sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
// Search the files in the user's Docs List for the search term
var startRow = 2; // First row of data to process
var numRows = sheet.getMaxRows() ; // Number of rows to process
//sheet.getRange(2, 3, 1, 1).setValue(numRows.toString());
var dataRange = sheet.getRange(startRow, 1, numRows-1)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
k = startRow
for (i in data) {
// sheet.getRange(2, 3, 1).setValue(data[i].toString());
var files = DocsList.find(data[i].toString()+".txt");
Utilities.sleep(1000);
for (var j = 0; j < files.length; j++) {
if (files[j].getType() == "other"){
urlBase = "https://docs.google.com/fileview?id=";
sheet.getRange(k, 8, 1, 1).setValue(urlBase + files[j].getId());
k = k+1
break;
}
}
}
}

Categories

Resources