I would like to do this in G column : If Cell 'G8' is Blank Then (=K8) for example. My difficulty is to make reference of same row in K of empty cell in G.
I've tryed to adapt this script but I get a shift at some point and I don't know why.
function updatewithformula() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet 1');
var range = sheet.getDataRange()
var source = sheet.getRange('G1:G13').getDisplayValues()
var index = []
for (var i = 1; i<source.length; i++){
if (source[i][0] == ""){
index.push(i+1)
}
}
index.push(range.getLastRow()+1)
Logger.log(index)
for(var i = 0;i<index.length-1;i++){
var rangetomodify = sheet.getRange(index[i],7,1,1)
var l = index[i+1]-index[i]-1
rangetomodify.setFormulaR1C1("=R["+l+"]C[4]")
}
}
As another approach, how about the following modification?
Modification points:
About I've tryed to adapt this script but I get a shift at some point and I don't know why., in your script, "=R[" + l + "]C[4]" is used with var l = index[i + 1] - index[i] - 1. In this case, when l is not 0, the other row is used. I thought that this might be the reason for your issue. In your script, I think that it is not required to use var l = index[i + 1] - index[i] - 1. When your script is simply modified, it becomes as follows.
function updatewithformula() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet 1');
var range = sheet.getDataRange();
var source = sheet.getRange('G1:G13').getDisplayValues();
var index = [];
for (var i = 1; i < source.length; i++) {
if (source[i][0] == "") {
index.push(i + 1);
}
}
index.push(range.getLastRow() + 1);
for (var i = 0; i < index.length - 1; i++) {
var rangetomodify = sheet.getRange(index[i], 7, 1, 1);
rangetomodify.setFormulaR1C1("=R[0]C[4]");
}
}
But, in this case, getRange and setFormulaR1C1 are used in a loop. In this case, the process cost becomes high.
When your script is modified by reducing the process cost, how about the following modification?
Modified script:
function updatewithformula() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet 1');
var source = sheet.getRange('G2:G13').getDisplayValues();
var ranges = source.reduce((ar, [g], i) => {
if (!g) ar.push(`G${i + 2}`);
return ar;
}, []);
sheet.getRangeList(ranges).setFormulaR1C1("=R[0]C[4]");
}
By RangeList, the process cost can be reduced a little.
References:
reduce()
getRangeList(a1Notations)
setFormulaR1C1(formula) of Class RangeList
There are better, easy and short methods to do it, I have used offset here.
The below works for me
function updatewithformula(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet 1');
var range = sheet.getDataRange()
var source = sheet.getRange('G1:G13').getValues();
Logger.log(source);
var index = [];
for (var i in source){
if(source[i][0] == ""){
var j = +i;
index.push(("G"+(j+1)));
}
}
for (var i in index){
var TEMP = sheet.getRange(index[i]);
var TEMP1 = TEMP.offset(0,4).getA1Notation();
const formula = `=${TEMP1}`;
TEMP.setFormula(formula);
}
}
References - OFFSET
I have a Google Ads script which pulls close variant keyword performance if it is above a ROAS threshold, and then exports it to a google sheet every 30 days. I'm then looking for a way for the script to take the keyword and ad group data and loop through the sheets, lookup the keywords and return the quality scores for these keywords after I have added them.
Here's my below code, I'm struggling with adding to a 2D array from the Google sheet (as you need a 2d array to look up keywords). I also need to be able to push the data back to the sheet. Any thoughts please?
var sheets = SpreadsheetApp.openByUrl(spreadsheetUrl).getSheets();
for (i = 0; i < sheets.length; i++) {
var sheet = sheets[i];
var data_range = sheet.getDataRange();
var last_row = data_range.getLastRow();
for(var r=2;r<=last_row;r++) {
var list = [];
var keywordID = [];
var adGroupId = [];
var keywordID = data_range.getCell(r,13).getValue();
var adGroupId = data_range.getCell(r,14).getValue();
list.push([adGroupId,keywordID]);
var keywords = AdsApp.keywords().withIds(list).get();
while (keywords.hasNext()) {
var keyword = keywords.next();
var stats = [];
var stats = keyword.getQualityScore();
row_array.push(stats);
sheet.appendRow(row_array)
}
}
}
var data_range = sheet.getDataRange(); is your 2D range
var data = sheet.getDataRange().getValues(); is your 2D array
You can loop through the data this way (no need the last_row):
for(var r=2; r<=data.length; r++)
Then
var keywordID = data[r][13]; // or [12], I don't know
var adGroupId = data[r][14]; // or [13]
Probably it would look about like this
var sheets = SpreadsheetApp.openByUrl(spreadsheetUrl).getSheets();
for (i = 0; i < sheets.length; i++) {
var sheet = sheets[i];
var data = sheet.getDataRange().getValues();
for (var r = 2; r <= data.length; r++) {
var keywordID = data[r][13]; // or [12]
var adGroupId = data[r][14]; // or [13]
var keywords = AdsApp.keywords().withIds([adGroupId, keywordID]).get(); // ?
var row_array = []
while (keywords.hasNext()) {
var stats = keywords.next().getQualityScore();
row_array.push(stats);
}
sheet.appendRow(row_array);
}
}
I haven't tested it since I have no your data and not quite understand the task.
I want to add the word "Flag" into column "G" or Array [6] where the corresponding row shows a value greater than 0.5 in column "E" or Array [5]. Note, that Array [6] is empty and only the script can add a value there if condition is met.
Here is my attempt but it does not add the word "Flag" into the cell.
I appreciate any help or pointer. Thanks in advance!
function test() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet1');
var rg=sh.getDataRange()
var vA=rg.getValues();
var g = [];
for(var i=1;i<vA.length;i++) {
g[i] = [vA[i][6]];
if(Number(vA[i][5])>0.5)g[i] = ['Flag']; {
SpreadsheetApp.getActiveSheet().getRange(2,7,g.length,1).setValues(g);
}}}
The error states:Cannot convert Array to Object[].
Here is an amended version of your code that will work if all your data is plain values and not formulas.
Please note this will overwrite any formulas.
function test() {
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName('Sheet1');
var rg = sh.getDataRange();
var vA = rg.getValues();
for (var i = 1; i < vA.length; ++i) {
if (Number(vA[i][5]) > 0.5) {
vA[i][6] = 'Flag';
//change value of array element
}
}
rg.setValues(vA);
//set changed values to source range
}
Edit
This checks column F and makes changes to column G.
It will not overwrite the formulas in column F, but it will overwrite any formulas in column G.
function test() {
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName('Sheet1');
var lastRow = sh.getLastRow();
var checkRg = sh.getRange('F2:F' + lastRow);
var flagRg = sh.getRange('G2:G' + lastRow);
var checkVa = checkRg.getValues();
var flagVa = flagRg.getValues();
for (var i = 0; i < checkVa.length; ++i) {
if (Number(checkVa[i][0]) > 0.5) {
flagVa[i][0] = 'Flag';
//change value of array element
}
}
flagRg.setValues(flagVa);
//set changed values to source range
}
Not sure what is in columns A thru F and beyond G.
for(var i=1;i<vA.length;i++) {
if(Number(vA[i][5])>0.5) vA[i][6] = 'Flag';
}
SpreadsheetApp.getActiveSheet().getDataRange.setValues(vA);
Or if you only want to replace G.
var g = [];
for(var i=1;i<vA.length;i++) {
g[i-1] = [vA[i][6]]; // Notice its an array
if(Number(vA[i][5])>0.5) g[i-1] = ['Flag']; // Notice an array again
}
SpreadsheetApp.getActiveSheet().getRange(2,7,g.length,1).setValues(g);
I've just started using Google Apps script to manage some sheets for a project i'm working on, I am new to Javascript so please go easy if there are any howlers in my code!.
We have and app called forms2mobile that captures data and drops it into a Google spreadsheet. It actually drops different data into different sheets depending on which part of the app you use.
I've hacked together a script that pulls all data from one sheet (source), and drops only certain columns into a second sheet (destination). It then deletes all rows from the source, and any blank rows from the destination.
The problem I have is with deleting blank rows from the destination. Typically the destination will have empty rows at the bottom, and the code I have will only delete empty rows within the range that contains data. So i'm always left with empty rows at the bottom.
The destination sheet will then be used as a data source for forms2mobile, which of course isn't happy with empty rows.
I've found the class getMaxRows() but i'm not sure how to implement it. If anyone could make any suggestions that would be great.
Cheers
Paul
function NEW_copyColumnNumbers( ) {
var spreadsheet_source = SpreadsheetApp.openById('1a89ZIUcy-8168D1damCV3Q9Ix0arQn9jGS6pgp');
var spreadsheet_target = SpreadsheetApp.openById('1GQiLt9utSH_6CV__oJwmcLOkI4E9iNIRPWU7Xr');
var range_input = spreadsheet_source.getRange("A2:CC407");
var range_output = spreadsheet_target.getRange("A"+(spreadsheet_target.getLastRow()+1));
var keep_columns = [66,66,10,11,12,13,14,23,26,31,69,71,74,75,80];
copyColumnNumbers(range_input, range_output, keep_columns);
clearEmptyRows();
clearSourceData();
}
function copyColumnNumbers( range_input, range_output, columns_keep_num ) {
// Create an array of arrays containing the values in the input range.
var range_values = range_input.getValues();
// Loop through each inner array.
for ( var i = 0, row_count = range_values.length; i < row_count; i++ ) {
// Loop through the indices to keep and use these indices to
// select values from the inner array.
for ( j = 0, col_keep_count = columns_keep_num.length; j < col_keep_count; j++ ) {
// Capture the value to keep
var keep_val = range_values[i][columns_keep_num[j]];
// Write the value to the output using the offset method of the output range argument.
range_output.offset(i,j).setValue(keep_val);
}
}
}
function clearEmptyRows() {
var ss = SpreadsheetApp.openById('1GQiLt9utSH_6CV__oJwmcLOkI4E9iNIRPWU7Xr');
var s = ss.getActiveSheet();
var values = s.getDataRange().getValues();
nextLine: for( var i = values.length-1; i >=0; i-- ) {
for( var j = 0; j < values[i].length; j++ )
if( values[i][j] != "" )
continue nextLine;
s.deleteRow(i+1);
}
//I iterate it backwards on purpose, so I do not have to calculate the indexes after a removal
}
function clearSourceData() {
var ss = SpreadsheetApp.openById('1a89ZIUcy-8168D1damCV3Q9Ix0arQn9jGS6pgp');
var s = ss.getActiveSheet();
var data = s.getDataRange().getValues();
for(var n =data.length+1 ; n<0 ; n--){
if(data[n][0]!=''){n++;break}
}
s.deleteRows(2, (s.getLastRow()-1));
}
This is how it works :
function removeEmptyRows(){
var sh = SpreadsheetApp.getActiveSheet();
var maxRows = sh.getMaxRows();
var lastRow = sh.getLastRow();
sh.deleteRows(lastRow+1, maxRows-lastRow);
}
Note : you can handle columns the same way if necessary using getMaxColumn(), getLastColumn() and deleteColumns(number, howMany)
EDIT
by the way, here is also another way to delete empty rows in a spreadsheet... if you combine both it will "clean" your sheet entirely !
function deleteEmptyRows(){
var sh = SpreadsheetApp.getActiveSheet();
var data = sh.getDataRange().getValues();
var targetData = new Array();
for(n=0;n<data.length;++n){
if(data[n].join().replace(/,/g,'')!=''){ targetData.push(data[n])};
Logger.log(data[n].join().replace(/,/g,''))
}
sh.getDataRange().clear();
sh.getRange(1,1,targetData.length,targetData[0].length).setValues(targetData);
}
Demo sheet in view only - make a copy to use
Script to removeEmptyRows and removeEmptyColumns in Google Sheets. It puts together everything Serge and apptailor mentioned previously. Here is a sample sheet with the script included File > Make a copy... to edit a copy of the sheet. Also a video that shows you how to use this sheet.
//Remove All Empty Columns in the Entire Workbook
function removeEmptyColumns() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
for (var s in allsheets){
var sheet=allsheets[s]
var maxColumns = sheet.getMaxColumns();
var lastColumn = sheet.getLastColumn();
if (maxColumns-lastColumn != 0){
sheet.deleteColumns(lastColumn+1, maxColumns-lastColumn);
}
}
}
//Remove All Empty Rows in the Entire Workbook
function removeEmptyRows() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
for (var s in allsheets){
var sheet=allsheets[s]
var maxRows = sheet.getMaxRows();
var lastRow = sheet.getLastRow();
if (maxRows-lastRow != 0){
sheet.deleteRows(lastRow+1, maxRows-lastRow);
}
}
}
Just a quick note, I added this "if" statement to keep Serge insas's code from throwing an error if there is no empty bottom row when you are trying to remove empty rows.
Place this if around the last line function removeEmptyRows() and it
will not throw an error:
if (maxRows-lastRow != 0){
sh.deleteRows(lastRow+1, maxRows-lastRow);
}
Removing all empty lines (bottom-up)
before
after
function isEmptyRow(row){
for (var columnIndex = 0; columnIndex < row.length; columnIndex++){
var cell = row[columnIndex];
if (cell){
return false;
}
}
return true;
}
function removeEmptyLines(sheet){
var lastRowIndex = sheet.getLastRow();
var lastColumnIndex = sheet.getLastColumn();
var maxRowIndex = sheet.getMaxRows();
var range = sheet.getRange(1, 1, lastRowIndex, lastColumnIndex);
var data = range.getValues();
sheet.deleteRows(lastRowIndex+1, maxRowIndex-lastRowIndex);
for (var rowIndex = data.length - 1; rowIndex >= 0; rowIndex--){
var row = data[rowIndex];
if (isEmptyRow(row)){
sheet.deleteRow(rowIndex + 1);
}
}
}
function removeEmptyLinesFromAllSheets(){
SpreadsheetApp.getActive().getSheets().forEach(removeEmptyLines);
}
Removing only empty lines from below and above the data
before
after
function isEmptyRow(row){
for (var columnIndex = 0; columnIndex < row.length; columnIndex++){
var cell = row[columnIndex];
if (cell){
return false;
}
}
return true;
}
function getFirstNonBlankRowIndex(data){
for (var rowIndex = 0; rowIndex < data.length; rowIndex++){
var row = data[rowIndex];
if (!isEmptyRow(row)){
return rowIndex;
}
}
return 0;
}
function removePaddedEmptyLines(sheet){
var lastRowIndex = sheet.getLastRow();
var lastColumnIndex = sheet.getLastColumn();
var maxRowIndex = sheet.getMaxRows();
var range = sheet.getRange(1, 1, lastRowIndex, lastColumnIndex);
var data = range.getValues();
var firstRowIndex = getFirstNonBlankRowIndex(data);
sheet.deleteRows(lastRowIndex+1, maxRowIndex-lastRowIndex);
sheet.deleteRows(1, firstRowIndex);
}
function removePaddedEmptyLinesFromAllSheets(){
SpreadsheetApp.getActive().getSheets().forEach(removePaddedEmptyLines);
}
I have tried this piece of code and it works good, you may take a look and try it:
function DeleteBlankRows(){
var sh = SpreadsheetApp.getActiveSheet();
var maxRows = sh.getMaxRows();
var lastRow = sh.getLastRow();
for (var Raw = 1; Raw < sh.getLastRow() ; Raw++)
{
if( sh.getRange('A'+Raw).getValue() == '')
{
sh.deleteRow(Raw) //deleteRows(lastRow+1, maxRows-lastRow);
}
}
This works perfectly for me.
function removeEmptyRows(){
var spreadsheet = SpreadsheetApp.openById("IDOFYOURSPREADSHEETFOUNDINURL");
var sh = SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[0]);
var maxRows = sh.getMaxRows();
var lastRow = sh.getLastRow();
sh.deleteRows(lastRow+1, maxRows-lastRow);
}
This version allows you to specify top rows you don't want removed and also to ignore columns after ignoreAfterCol in case you don't want some columns considered when you are looking for blanks:
function removeEmptyLines(sheet,ignoreFirstRows,ignoreAfterCol){
sheet=ss.getSheetByName('Sheet12')
//get data and boundaries
var allData = sheet.getRange(1,1,sheet.getMaxRows(),ignoreAfterCol).getValues();
var sheetLength = allData.length;
while(allData[allData.length-1].toString().replace(/,/g,'')=='') allData.pop();
var lastPopulatedRow = allData.length;
//delete empty rows from end
var rowsToDeleteFromEnd = sheetLength - lastPopulatedRow;
if(rowsToDeleteFromEnd > 0) sheet.deleteRows(lastPopulatedRow+1,rowsToDeleteFromEnd);
//iterate through rows and delete blanks one by one
for(var i=lastPopulatedRow-1; i>ignoreFirstRows; i--){
if(allData[i].toString().replace(/,/g,'')=='') sheet.deleteRow(i+1);
}
}
this will help to delete exactly what you want:
Plus point:
you can check as many columns as you want to identify if a row is empty
this will also delete blank rows that contain formula
improve performance: this script deletes directly the empty rows according to their position without iteration through all the rows.
function deleteBlankRows(start_row=4) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
//temporarily insert last column to avoid affecting existing data
sheet.insertColumnsAfter(sheet.getMaxColumns(),1);
var lastRow = findLastRow();
var lastCol = sheet.getMaxColumns()
var temp_col = sheet.getRange(start_row,lastCol,lastRow-start_row,1)
//insert formula to show row position if any row is blank from column A to N (can adjust if needed)
sheet.getRange(start_row,lastCol).setFormula('=if(countif(A'+start_row+':N'+start_row+',"*?")=0,row(),0)').copyTo(temp_col)
//get a reversed list of rows position excluded non-empty rows
var rowsPosition = temp_col.getValues().filter(x => x != 0).reverse()
//delete empty rows from bottom to top
rowsPosition.forEach(function(rowPosition){
if (Number(rowPosition) > start_row) {
sheet.deleteRow(Number(rowPosition))
}
})
//finally, delete the temporary column
sheet.deleteColumn(lastCol)
}
function findLastRow() {
const sh = SpreadsheetApp.getActive().getActiveSheet();
const data = sh.getRange("A:L").getValues();
const mR = sh.getMaxRows();
const indexes = [];
data[0].forEach((_, ci) => {
let col = data.map(d => d[ci]);
let first_index = col.reverse().findIndex(r => r != '');
if (first_index != -1) {
let max_row = mR - first_index;
indexes.push(max_row);
}
});
last_row = indexes.length > 0 ? Math.max(...indexes) : 0;
return last_row;
}
function deleteblankRw(){
var sheet=SpreadsheetApp.getActive().getSheetByName('test')
var e=sheet.getRange('A'+sheet.getMaxRows()).getNextDataCell(SpreadsheetApp.Direction.UP).getRow()
for (k=2;k<=e;k++) {
if(sheet.getRange('A'+k).getValue()=='') {
sheet.deleteRow(k);
k=2;e--
if(k==e){break};
SpreadsheetApp.flush();
}
}
}
I am trying to make a script that will create a table from a spreadsheet with a list of suppliers. The spreadsheet has over a thousand entries, so my script is processing it very slowly. Here is the code
function SupplerAnalysis() {
//Importing data
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
//Creating a sheet
if (ss.getSheetByName("Analysis") !=null) {
//Logger.log('exists');
} else {
//Logger.log('Creating new');
ss.insertSheet("Analysis");
}
var sheetNumber = ss.getSheetByName("Analysis").getIndex() - 1;
ss.getSheetByName("Analysis").clear();
var newsheet = ss.getSheets()[sheetNumber];
var newdata = newsheet.getDataRange().getValues();
newsheet.getRange(1, 1).setValue('Suppliers');
//Get list of suppliers
for (var s = 1; s < data.length; s++) {
var supplier = data[s][3];
var z = 1;
newdata = newsheet.getDataRange().getValues();
for (var r = 1; r < newdata.length;r++) {
if (supplier === newdata[r][0]) {
z = 2;
} else { Logger.log(r);}
}
if (z === 1) {
newsheet.getRange(r+1, 1).setValue(supplier);
} else if ( z > 1 ) { Logge.log('Error');
}
}
Column data[s][3] is the list of suppliers for different jobs. There are more than thousand entries, and about 160 suppliers in total. This script takes about 5 minutes to execute, which is very slow and ineffective.
How can I change the code to speed up this process?
Is there any way to get the output from the NewCategoryFilter in to a table?
Just don't call the API in loops:
function SupplerAnalysis() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var newsheet;
if ((newsheet=ss.getSheetByName("Analysis")) == null)
newsheet = ss.insertSheet("Analysis");
else newsheet.clear();
var supplierList = [['Suppliers']];
for (var s = 1; s < data.length; s++) {
for (var r = 1; r < supplierList.length; r++)
if (data[s][3] == supplierList[r][0]) break;
if (r == supplierList.length) supplierList.push([data[s][3]]);
}
newsheet.getRange(1,1,supplierList.length,1).setValues(supplierList);
}
Keep your eyes open on typos - I just typed this in.
Converting suppliers in object keys could be somewhat risky as the supplier names maybe are not legal object keys.
Getting rid of the call to newsheet.getDataRange inside your loop will probably speed things up. You can check for duplicates faster by storing values as object {} keys and using the "in" operator to see if they are present. e.g.
var set = {};
vals.forEach(function(value){
if(value in set) {
console.log('duplicate found');
}
set[value] = true;
});
I'm not sure what NewCategoryFilter is.