Creating a range and reassign to other sheet - google-apps-script

I build a range that I am trying to reassign from one sheet to another, but it fails with an error.
function test() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName('Sheet1');
var destinationSheet = ss.getSheetByName('Sheet2');
thisWeek = 37;
var firstWeek = [];
var unprotectRange = [];
firstWeek[0] = sourceSheet.getRange('E6:E12');
firstWeek[1] = sourceSheet.getRange('F8:F10');
const columnsPerWeek = 51;
for (var k = thisWeek - 1; k < 53; k++) {
for (var l = 0; l < firstWeek.length; l++) {
if (
sourceSheet
.getRange(firstWeek[l].offset(0, columnsPerWeek * k).getA1Notation())
.isBlank()
) {
unprotectRange.push(
sourceSheet.getRange(
firstWeek[l].offset(0, columnsPerWeek * k).getA1Notation()
)
);
}
}
}
var range2 = destinationSheet.getRange(unprotectRange.getA1Notation());
var protection = destinationSheet.protect().setDescription('Maintain');
protection.setUnprotectedRanges(range2);
}
Now I get the error: 'TypeError: unprotectRange.getA1Notation is not a function' at the statement
var range2 = destinationSheet.getRange(unprotectRange.getA1Notation());

unprotectRange is a array of ranges from source sheet. They need to be strings(A1Notations). Push only the A1 notations.
Convert the strings(A1notations) to ranges of destination sheet using array.map
setProtectedRanges() will then accept this array.
/*...*/
if (
sourceSheet
.getRange(firstWeek[l].offset(0, columnsPerWeek * k).getA1Notation())
.isBlank()
) {
unprotectRange.push(
firstWeek[l].offset(0, columnsPerWeek * k).getA1Notation()/*Modified*/
);
}
/*...*/
const ranges2 = unprotectRange.map(a1Notation => destinationSheet.getRange(a1Notation));
const protection = destinationSheet.protect().setDescription('Maintain');
protection.setUnprotectedRanges(ranges2);
protection.removeEditors(protection.getEditors())

Related

Exception: The number of rows in the data does not match the number of rows in the range. The data has 1 but the range has 8. Google Script

I am having troubles in
Exception: The number of rows in the data does not match the number of rows in the range. The data has 1 but the range has 8. Google Script
function myFunction() {
var s1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Staging');
var s2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Final');
var values1 = s1.getDataRange().getValues();
var values2 = s2.getDataRange().getValues();
var Avals = s2.getRange("A1:A").getValues();
var Alast = Avals.filter(String).length;
var resultArray = [];
for(var n=0; n < values1.length ; n++)
{
var keep = false;
var counter = 0;
for(var p=0; p < values2.length ; p++)
{
if( values1[n][1] == values2[p][1])
{
keep = true;
break ;
}
}
if(keep == false)
{
resultArray.push([values1[n]]);
//s2.appendRow( values1[n] );
s2.getRange(Alast + 1, 1, values1[n].length, values1[n].length).setValues([values1[n]]);
//s2.getRange(s2.getLastRow() + 1, 1, values1.length, values1[0].length).appendRow( values1[n] );
resultArray = [];
keep = false
}
}
}
I believe you are trying to keep appending a new row to the end of the data on "Final". You should revise you script as shown below. Use resultArray to collect all new rows and save it to spreadsheet once.
function myFunction() {
var s1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Staging');
var s2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Final');
var values1 = s1.getDataRange().getValues();
var values2 = s2.getDataRange().getValues();
// s2.getDataRange() will get up to the last row of data
var Alast = values2.length;
var resultArray = [];
for(var n=0; n < values1.length ; n++)
{
var keep = false;
var counter = 0;
for(var p=0; p < values2.length ; p++)
{
if( values1[n][1] == values2[p][1])
{
keep = true;
break ;
}
}
if(keep == false)
{
resultArray.push(values1[n]);
keep = false
}
}
if( resultArray.length > 0 ) {
s2.getRange(Alast+1,1,resultArray.length,resultArray[0].length).setValues(resultArray);
}
}

How to count only filtered cells within a function that counts cells of a certain color

This is my function for counting colored cells within a sheet. However, I need to add another variable which counts only the cells displayed, after filtering the data.
function countColoredCells(countRange,colorRef) {
var activeRange = SpreadsheetApp.getActiveRange();
var activeSheet = SpreadsheetApp.getActiveSheet();
var activeformula = activeRange.getFormula();
var countRangeAddress = activeformula.match(/\((.*)\,/).pop().trim();
var backGrounds = activeSheet.getRange(countRangeAddress).getBackgrounds();
var colorRefAddress = activeformula.match(/\,(.*)\)/).pop().trim();
var BackGround = activeSheet.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 I filter the raw data, it still displays the count of ALL data. I then tried to nest the function within the following function:
function applyOverdueFilter() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getDataRange().getDisplayValues();
var newData= ss.filter(function countColoredCells(countRange,colorRef) {
var activeRange = SpreadsheetApp.getActiveRange();
var activeSheet = SpreadsheetApp.getActiveSheet();
var activeformula = activeRange.getFormula();
var countRangeAddress = activeformula.match(/\((.*)\,/).pop().trim();
var backGrounds = activeSheet.getRange(countRangeAddress).getBackgrounds();
var colorRefAddress = activeformula.match(/\,(.*)\)/).pop().trim();
var BackGround = activeSheet.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;
}
Any help is appreciated,

What to change in my function to change the range it will archive?

I was able to get the script to log a single cell. However, I am looking now to log 2 columns with 49 rows and then add the date timestamp to the third column. What would I need to change within the script? I have been changing the numerical values and no matter what, its still looking for 1 cell. Is it a greater change than I thought?
function DHLtracking() {
const ss = SpreadsheetApp.openById('Sample_ID_1');
const sh = ss.getSheetByName('DHL Shipping Data');
const data = [[sh.getRange('C3:D51').getValue(),new Date()]];
const tss = SpreadsheetApp.openById('Sample_ID_1');
const ts = tss.getSheetByName('Archived Data');
ts.getRange(getColumnHeight() + 1, 8, data.length, data[0].length).setValues(data);
}
function getColumnHeight(col, sh, ss) {
var ss = ss || SpreadsheetApp.openById('Sample_ID_1');
var sh = sh || ss.getSheetByName('Archived Data');
var col = col || 8;
const rcA = sh.getRange(1, col, sh.getLastRow(), 1).getValues().flat().reverse()
let s = 0;
for (let i = 0; i < rcA.length; i++) {
if (rcA[i].toString().length == 0) {
s++;
} else {
break;
}
}
return rcA.length - s;
}
Try this:
function DHLtracking() {
const ss = SpreadsheetApp.openById('Sample_ID_1');
const sh = ss.getSheetByName('DHL Shipping Data');
const sr = 3;
const sc = 3;
const dt = new Date();
const data = sh.getRange(sr, sc, 49, 3).getValues().map(r => r[2] = dt)
const tss = SpreadsheetApp.openById('Sample_ID_1');
const tsh = tss.getSheetByName('Archived Data');
tsh.getRange(tsh.getLastRow() + 1, 8, data.length, data[0].length).setValues(data);
}

How to increase speed of my script containing loops in Google Sheets?

I have an issue with a slow speed of my Google Apps Script containing a loop. I'm looking for recommendations how to increase the speed.
I have the following input data on a sheet named "Test" (just an extract for demonstration purposes but have in mind that the original data is much larger - with much more products and suppliers):
I want to achieve on a separate sheet named "Suppliers" the following result:
The idea is the suppliers with their respective products and prices to be reorganized in the format shown above. The conditions are:
Exclude inactive products;
Exclude inactive suppliers;
include only products per supplier with an available price;
Dynamic update of the data when adding new products/ suppliers, or changing anything on the input table.
I have done the following script - it's working, but speed is quite slow considering the fact that original data is large. I would highly appreciate any advises how to optimize it. I spent much time trying to optimize it by myself but to no avail. Many thanks in advance!
function Suppliers() {
var file = SpreadsheetApp.getActiveSpreadsheet();
var sheetSourceName = 'Test';
var sheetDestinationName = 'Suppliers';
var sourceSheet = file.getSheetByName(sheetSourceName);
var numRows = sourceSheet.getLastRow();
var numCols = sourceSheet.getLastColumn();
var destinationSheet = file.getSheetByName(sheetDestinationName);
var sheet = file.getActiveSheet();
if( sheet.getSheetName() == sheetSourceName ) {
var lastRow = destinationSheet.getLastRow();
destinationSheet.getRange( 2, 1, lastRow, 3 ).clear( { contentsOnly : true } );
var lastRow = 1;
for( var s = 3; s < numCols + 3; s++ ){
for( var p = 3; p < numRows + 3; p++ ){
var product = sourceSheet.getRange( p, 2 ).getValues();
var activeProduct = sourceSheet.getRange( p, 1 ).getValues();
var price = sourceSheet.getRange( p, s ).getValues();
var supplier = sourceSheet.getRange( 2, s ).getValues();
var activeSupplier = sourceSheet.getRange( 1, s ).getValues();
if( activeProduct == "active" && price > 0 && activeSupplier == "active" ){
lastRow = lastRow + 1;
destinationSheet.getRange( lastRow, 1 ).setValues( product );
destinationSheet.getRange( lastRow, 2 ).setValues( price );
destinationSheet.getRange( lastRow, 3 ).setValues( supplier );
}
}
}
}
}
No guarantee because I don't have a data sheet set up to match your situation but I think this will work for you. Try it in a copy of your spreadsheet. Note that in your getRange you use column and row which start with 1. I'm loading everything into an array a looping through its values where the index starts with 0. So it becomes your row/col -1.
function Suppliers() {
var file = SpreadsheetApp.getActiveSpreadsheet();
var sheetSourceName = 'Test';
var sheetDestinationName = 'Suppliers';
var sourceSheet = file.getSheetByName(sheetSourceName);
var numRows = sourceSheet.getLastRow();
var numCols = sourceSheet.getLastColumn();
var destinationSheet = file.getSheetByName(sheetDestinationName);
var sheet = file.getActiveSheet();
if( sheet.getSheetName() == sheetSourceName ) {
destinationSheet.getRange( 2, 1, lastRow, 3 ).clear( { contentsOnly : true } );
var sourceData = sourceSheet.getRange(1,1,sourceSheet.getLastRow(),sourceSheet.getLastColumn()).getValues();
var destinationData = [];
// Not sure why +3 added to numRows and numCols because there is no data there
for( var s=2; s<numCols; s++ ) {
for( var p=2; p<numRows; p++ ) {
var product = sourceData[p][1];
var activeProduct = sourceData[p][0];
var price = sourceData[p][s];
var supplier = sourceData[1][s];
var activeSupplier = sourceData[0][s];
if( activeProduct == "active" && price > 0 && activeSupplier == "active" ){
destinationData.push([product,price,supplier]);
}
}
}
destinationSheet.getRange(2,1,destinationData.length,3).setValues(destinationData);
}
}

Google script - copy columnC data from Sheet21 to columnC Sheet2, if not present in Sheet2 already

I am writing one of my first scripts, and have tried to look at other similar questions.
I have two sheets:
sheet1: new data (Only Column C is of interest, everything else can ignore in other columns)
sheet2: old data (but needs to be updated with sheet1 new data if not already there). The data to be added should be at the end of Column C after the existing data.
The code I have has the following compiling error.
I need to - get the last row of Column C Sheet2. Then check if Column C sheet1 is present in ColumnC sheet2, if not present- copy over from sheet1 to sheet2 column C.
UPDATED CODE:
function updateSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = "Sheet1";
var destinationSheet = "Sheet2";
var source_sheet = ss.getSheetByName(sourceSheet);
var target_sheet = ss.getSheetByName(destinationSheet);
var last_row = CountColC();
//assumes headers in row 1
var r = target_sheet.getRange(1,2, lastRow - 1);
//Note the use of an array
r.sort([{column: 3, ascending: true}]);
// Process sheet
_updateSpreadsheet(source_sheet, target_sheet);
}
//gets last row in Column C
function CountColC(){
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for(var i = data.length-1 ; i >=0 ; i--){
if (data[i][2] != null && data[i][2] != ''){
return i+1 ;
}
}
}
function _updateSpreadsheet(source_sheet, target_sheet) {
var last_row = CountColC();
var source_data = source_sheet.getDataRange().getValues();
var target_data = target_sheet.getDataRange().getValues();
var resultArray = [];
for (var n = 1 ; n < source_data.length ; n++) {
var keep = true;
for(var p = 1 ; p < target_data.length ; p++) {
if (new Date(source_data[n][2]).getTime() == new Date(target_data[p][2]).getTime()) {
keep = false; break;
}
}
Logger.log(keep);
if(keep){ resultArray.push([source_data[n][2]])};
}
last_row++;
Logger.log(resultArray);
target_sheet.getRange(last_row,1,resultArray.length,resultArray[2].length).setValues(resultArray);
// target_data.push(n);
}
Thanks in advance :)
function updateSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = "Sheet1";
var destinationSheet = "Sheet2";
var source_sheet = ss.getSheetByName(sourceSheet);
var target_sheet = ss.getSheetByName(destinationSheet);
var lastCol = target_sheet.getLastColumn();
var lastRow = target_sheet.lastColumn();
//assumes headers in row 1
var r = target_sheet.getRange(2,1, lastRow - 1, 3);
//Note the use of an array
r.sort([{column: 3, ascending: true}]);
// Process sheet
_updateSpreadsheet(source_sheet, target_sheet);
}
//gets last row in Column C
function CountColC(){
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for(var i = data.length-1 ; i >=0 ; i--){
if (data[i][2] != null && data[i][2] != ''){
return i+1 ;
}
}
}
function _updateSpreadsheet(source_sheet, target_sheet) {
var last_row = target_sheet.CountColC();
var source_data = source_sheet.getDataRange().getValues();
var target_data = target_sheet.getDataRange().getValues();
var resultArray = [];
for (var n = 1 ; n < source_data.length ; n++) {
var keep = true;
for(var p = 1 ; p < target_data.length ; p++) {
if (new Date(source_data[n][2]).getTime() == new Date(target_data[p][2]).getTime()) {
keep = false; break;
}
}
Logger.log(keep);
if(keep){ resultArray.push([source_data[n][2]])};
}
last_row++;
Logger.log(resultArray);
target_sheet.getRange(last_row,1,resultArray.length,resultArray[2].length).setValues(resultArray);
// target_data.push(n);
}
Instead call
var last_row = target_sheet.CountColC();
Please use
var last_row = CountColC();