Iterate through named range and update values - google-apps-script

I need to iterate through a named range in Google Sheet and update the value based on other named ranges.
I have this now and I'm getting an error on line 13
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var FreeCopiesTarget = ss.getRangeByName('FreeCopiesTarget');
var CostPerCopy = ss.getRangeByName('CostPerCopy');
var FreeCopies = ss.getRangeByName('FreeCopies');
var values = FreeCopiesTarget.getValues();
for (var counter = 0; counter <= 4; counter++) {
var cost = CostPerCopy.getValues()[counter][0] * FreeCopies.getValues()[counter][0];
var r = FreeCopiesTarget.getValues()[counter][0];
r.setValue(cost);
}
}
Any help is appreciated.

Issue:
r is a value from a cell, which can be a Number, Boolean, String, or a Date. None of these includes the method setValue. r should be a Range instead.
Code sample:
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var FreeCopiesTarget = ss.getRangeByName('FreeCopiesTarget');
freeCopiesValues = FreeCopiesTarget.getValues();
var CostPerCopy = ss.getRangeByName('CostPerCopy').getValues();
var FreeCopies = ss.getRangeByName('FreeCopies').getValues();
for (var counter = 0; counter <= 4; counter++) {
var cost = CostPerCopy[0][counter] * FreeCopies[0][counter];
freeCopiesValues[0][counter] = cost;
}
FreeCopiesTarget.setValues(freeCopiesValues);
}
Note:
It's not best practice to use getValues iteratively. It's preferrable to get all values at once and work with the resulting 2D array, as shown in the sample above.

Following Iamblichus answer, I tweaked as follows and it works as expected.
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var FreeCopiesTarget = ss.getRangeByName('FreeCopiesTarget');
freeCopiesValues = FreeCopiesTarget.getValues();
var CostPerCopy = ss.getRangeByName('CostPerCopy').getValues();
var FreeCopies = ss.getRangeByName('FreeCopies').getValues();
for (var counter = 0; counter <= 4; counter++) {
var cost = CostPerCopy[0][counter] * FreeCopies[0][counter];
freeCopiesValues[0][counter] = cost;
}
FreeCopiesTarget.setValues(freeCopiesValues);
}

Related

Script splits text and adds 1 to value doesnt record the correct number

For the life of me I dont know what is wrong with this code it records the correct number but doesn't add the number correctly unless i type it in myself.
https://docs.google.com/spreadsheets/d/1kxIzE_HcPWd82LpGqCXNxg0yczhcnA6yOYRnB4GuAZo/edit?usp=sharing
var column = 0;
var HEADER_ROW_COUNT = 1;
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var worksheet = spreadsheet.getSheetByName("RawData");
var rows = worksheet.getDataRange().getNumRows();
var vals = worksheet.getSheetValues(1, 1, rows, 1);
var max = 0;
for (var row = HEADER_ROW_COUNT; row < vals.length; row++) {
var splitstring = vals[row][0].toString().split(" ");
var splitstring = vals[row][0].toString().split(" ");
var id = splitstring[1];
Logger.log(id)
if (max < id) {
Logger.log(id)
max = id+1;
Logger.log(max)
}
}
var LastRow = worksheet.getLastRow();
worksheet.getRange(LastRow+1,1).setValue("PO# " + max.toString());
Although I'm not sure whether I could correctly understand your goal, in your script, var id = splitstring[1]; retrieved by var splitstring = vals[row][0].toString().split(" "); is the string type. I thought that this might be the reason of your issue. When this is reflected to your script, it becomes as follows.
From:
var id = splitstring[1];
Logger.log(id)
if (max < id) {
To:
var id = Number(splitstring[1]);
Logger.log(id)
if (max <= id) {
Note:
In your script, for example, you might be also able to use var max = Math.max(...vals.map(([v]) => Number(v.split(" ").pop()))) + 1; instead of your for loop.
Reference:
split()
Tryu this:
function myfunk() {
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName("RawData");
Logger.log(parseInt(sh.getRange(sh.getLastRow(), 1).getValue().split(' ')[1]) + 1);
}

Data validation Script loop

I have a code to create a data validation on a cell from a range next to it.
example:
var cellC4 = cell.getRange('F11');
var range = SpreadsheetApp.getActive().getRange('MAIN!AG11:AG11');
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(range).build();
cellC4.setDataValidation(rule);
var cellC5 = cell.getRange('F12');
var range = SpreadsheetApp.getActive().getRange('MAIN!AG12:AG12');
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(range).build();
cellC5.setDataValidation(rule);
var cellC6 = cell.getRange('F13');
var range = SpreadsheetApp.getActive().getRange('MAIN!AG13:AG13');
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(range).build();
cellC6.setDataValidation(rule);
var cellC7 = cell.getRange('F14');
var range = SpreadsheetApp.getActive().getRange('MAIN!AG14:AG14');
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(range).build();
cellC7.setDataValidation(rule);
can someone help me do it correctly
Im stuck here:
function onOpen(){
var ss0 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('MAIN');
var EndRow = ss0.getLastRow();
for ( var c = 16;c <= 25; c) {
for ( var i = 11;i <= EndRow; i++ ) {
//►PO# VALIDATION►
var range1 = ss0.getRange(i, c);
var rule1 = SpreadsheetApp.newDataValidation().requireValueInRange(range1).build();
ss0.getRange(i, 5).setDataValidation(rule1);
}}}
Findings:
Your looping for this part for ( var c = 16;c <= 25; c) { has a
wrong iteration with c instead of using c++. Thus, this looping
will never finish running.
SUGGESTION:
==UPDATE===
You can try this sample script below:
function onOpen(){
var ss0 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('MAIN');
var EndRow = ss0.getLastRow();
for(row=11; row<=EndRow; row++){
var data = ss0.getRange("P"+row+":Y"+row).getDisplayValues();
var rule = SpreadsheetApp.newDataValidation().requireValueInList(data[0]).build();
ss0.getRange(row,5).setDataValidation(rule);
}
}
Sample:
Result on column 5 or column E after the onOpen() function finishes running

Script: Transpose, Find and Save

I have some data containing, Dates, Usernames and an average percent that i want to save in a certain way. My problem is that the order of the usernames can change depending on if new ones are added. I therefore need to "find" a specific username and then save the percentage data in the correct column.
I have found some code that partially helps me save the data that i need. But i could use some help in the "find" the corresponding username and save it in a certain Column part.
function save() {
var sss = SpreadsheetApp.getActive();
var ss = sss.getSheetByName('Result');
var range = ss.getRange('B1:B10');
var data = range.getValues();
var tss = SpreadsheetApp.getActive();
var ts = tss.getSheetByName('Archive');
ts.getRange(ts.getLastRow()+1, 1,data[0].length,data.length)
.setValues(Object.keys(data[0]).map ( function (columnNumber) {
return data.map( function (row) {
return row[columnNumber];
});
}));
}
Basically from this:
To a result that looks like this:
Thank you for your assistance.
Alright for anyone out there that may have a similar problem, this is what i ended up with.
function extractAttendance() {
var currentSheet = SpreadsheetApp.getActive();
var attendanceTab = currentSheet.getSheetByName('Data_Filtered');
var userData = attendanceTab.getRange('B1:B').getValues();
var percentageData = attendanceTab.getRange('I1:I').getValues();
var archiveTab = currentSheet.getSheetByName('Archive');
var existingUsersRow = archiveTab.getRange('1:1');
var newRowNumber = archiveTab.getLastRow() + 1;
archiveTab.getRange(newRowNumber, 1).setValue(new Date());
for (var i = 1; i < userData.length; i++) {
var user = userData[i][0];
if (user === '') {
continue;
}
var existingUsers = existingUsersRow.getValues()[0];
var exists = false;
var existingColumnNumber = -1;
for (var j = 0; j < existingUsers.length; j++) {
if (existingUsers[j] === user) {
exists = true;
existingColumnNumber = j + 1;
break;
}
}
if (exists) {
archiveTab.getRange(newRowNumber, existingColumnNumber).setValue(percentageData[i]);
} else {
var newColumnNumber = archiveTab.getLastColumn() + 1;
archiveTab.getRange(1, newColumnNumber).setValue(user);
archiveTab.getRange(newRowNumber, newColumnNumber).setValue(percentageData[i]);
}
}
}
It might be easier to implement your desired functionality through looping rather than through mapping.
The following code retrieves all users ad their percentage data in ‘Result’ and transfers the data (in the format you desire) to "Archive" with the percentages data pasted with the corresponding timestamp into the first empty row.
function save() {
var sss = SpreadsheetApp.getActive();
var ss = sss.getSheetByName('Result');
var range = ss.getRange('B1:B');
var percentageRange = ss.getRange('G1:G');
var userData = range.getValues();
var percentageData = percentageRange.getValues();
var tss = SpreadsheetApp.getActive();
var ts = tss.getSheetByName('Archive');
var userRow=1;
var percentageRow=(ts.getLastRow()+1)
for(var i=0; i<=userData.length; i++)
{
{
var j=(i+2);
ts.getRange(userRow, j).setValue(userData[i])
ts.getRange(percentageRow, 1).setValue(new Date())
ts.getRange(percentageRow, j).setValue(percentageData[i])
}
}
}

AInternal Error executing custom function

Is there any limitation in Apps Script?
I'm getting "internal Error" if I set variable "lLen" more then 18 - http://prntscr.com/j60kxb
Actually I need to have this string as var lLen = CIDlist.length; but I'm getting the Error above. In some cases CIDlist.length value can be 160+. When I played to understand the reason of the issue I found that it works if lLen <= 18. Any ideas why it happens?
function myFunction(input) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var sheet = sheets[3];
var lastrow = sheet.getLastRow();
var CIDlist = [];
for(var i =16; i<=lastrow; i++){
var firstval = sheet.getRange("B"+i).getValue();
var secondval = sheet.getRange("C"+i).getValue();
if (firstval == input[0][1] && secondval == input[0][0]) {
var CID = sheet.getRange("A"+i).getValue();
if (CIDlist.indexOf(CID) == -1) {
CIDlist.push(CID);
}
}
}
console.log(input);
console.log(CIDlist.length);
var lLen = 19;
var TotalRevenue = 0;
for (var i=0; i< lLen; i++){
var CIDvalue = CIDlist[i];
for (var j=16; j<=lastrow; j++){
var cid = sheet.getRange("A"+j).getValue();
var revenue = sheet.getRange("D"+j).getValue();
if (cid == CIDvalue) {
TotalRevenue = TotalRevenue + revenue;
}
}
}
return TotalRevenue;
}
The function on the question makes use of two for loops that makes several calls to the SpreadsheetApp classes.
As it fails when is increased a value that control the number of iterations of one of the for loops, which makes that the time execution of the custom function be increased, it's very likely that it's exceeding the 30 second limit for custom functions.

compare rows on google spreadsheets

I want to identify equal rows in two different sheets on same spreadsheet. I tried below code, it doesn't work.
function getMyEqualRows()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheets()[0];
var sheet2 = ss.getSheets()[1];
var sheetOneValues = sheet1.getRange('A1:G20').getValues();
var sheetTwoValues = sheet2.getRange('A1:G20').getValues();
var equalRows = [];
for (var i = 0; i <= sheetOneValues.length; i++) {
for(var j = 0; j <= sheetTwoValues.length; j++){
if(sheetOneValues[i] == sheetTwoValues[j]) {
equalRows.push(sheetOneValues[i]);
}
}
return equalRows;
}
}
You can't compare two arrays for equality with ==. Instead you can use .join() method. It concatenates all elements in these arrays to a string. This check string arrays for equality. I'm not sure if this is the best way.
function getMyEqualRows()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheets()[0];
var sheet2 = ss.getSheets()[1];
var sheetOneValues = sheet1.getRange('A1:G20').getValues();
var sheetTwoValues = sheet2.getRange('A1:G20').getValues();
var equalRows = [];
for(var i in sheetOneValues)
{
var anEqualRow = false;
for(var j in sheetTwoValues)
{
if(sheetOneValues[i].join() == sheetTwoValues[j].join()){
anEqualRow = true;
}
}
if(anEqualRow){
equalRows.push(sheetOneValues[i]);
}
}
return equalRows;
}
Hope this would work!