I am trying to make a script button that will iterate through all rows and simply subtract cell G from cell E if G has a numerical value in it, then clear cell G.
I am having issues with trying to get only column G to see if it contains a numerical value, normally column G will be blank
I have tried getting the range specified but it is not sticking to the G column and is going everywhere. And I think I am probably not going about this the proper way. Right now I am just trying to get the cells with data in the G column to highlight so I know if I am even searching the right cells
function calculate() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var rangeData = sheet.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
var searchRange = sheet.getRange("G1:G300");
var substring = "";
// Get array of values in the search Range
var rangeValues = searchRange.getValues();
// Loop through array and if condition met, add relevant
// background color.
for ( i = 0; i < lastColumn - 1; i++){
for ( j = 0 ; j < lastRow - 1; j++){
if(rangeValues[j][i] != ""){
sheet.getRange(j+1,i+7).setBackground("#cc4125");
};
};
};
};
Try this -
function calculate() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var sValues = sheet.getDataRange().getValues();
// E = 5, in array it is 4
// G = 7, in array it is 6
// Loop through array and if condition met, do stuff
sValues.forEach(function(row, i) {
if (row[6] != '') {
row[4] = row[4] - row[6];
row[6] = '';
sheet.getRange(i + 1, 7).setBackground('#cc4125');
}
});
sheet.getDataRange().setValues(sValues);
}
Thank you ra89fi, you gave me 99% of what I needed, here is the end result that worked
function calculate() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var sValues = sheet.getDataRange().getValues();
// E = 5, in array it is 4
// G = 7, in array it is 6
// Loop through array and if condition met, do stuff
sValues.forEach(function(row, i) {
if (row[6] != '') {
row[4] = row[4] - row[6];
row[6] = '';
sheet.getRange(i + 1, 5).getCell(1,1).setValue(row[4]);
sheet.getRange(i + 1, 7).getCell(1,1).setValue('');
}
});
}
Related
I currently have a script that takes the selected option in column F and updates a different sheet with the selected value. The first sheet is set up like this
A
B
C
D
E
F
Date
Names of other sheets
Month
Day
Weekday
Options
The other sheets are set to start on the 16th of the month and finish on the 15th of the following month. So the sheet labeled (2023/01) has dates starting from row 7. The dates are in column A and the value to be updated is in column D.
The problem
In the script, the dates are all offset by one. So if you select 23/01/16 it actually matches 23/01/17. This doesn't seem to be a problem except when the 15th of every month is selected. Since the sheets end on the 15th and the dates are offset it matches the 16th but the 16th does not exist on the sheet so the cell is not updated.
MY solution
To fix this issue I created a condition to check if the selected date includes '/16' then run some code. I then created a variable to get the previous sheet and got the last row and set the value that way. I used toast() to check if the values I am selecting are correct and they seem to be. The sheet name is correct and the last row is correct but I am not actually seeing the cell being updated. I am not sure what I am doing wrong so any help would be greatly appreciated.
/* 休日入力イベント
--------------------------------------*/
function changeHoliday(ss) {
var sheet = ss.getActiveSheet(); //アクティブなシート
var sheetName = sheet.getSheetName();
var atvc = sheet.getActiveCell(); //アクティブセル
//休日シートの休日を変更した時だけ
if(sheetName=='休日' || sheetName && atvc.getColumn() == 6){
var flag = atvc.getValue(); //休日かどうか
var targetSheetName = String(atvc.offset(1, -4).getValue()); //対応するシート名
//Get previous sheet name
var prevSheetName = String(atvc.offset(-1,-4).getValue());
var targetDate = Utilities.formatDate(atvc.offset(1, -5).getValue(),"JST", "yyyy/MM/dd"); //対応する日付
// var targetDateEndofSheet = Utilities.formatDate(atvc.offset(0, -5).getValue(),"JST", "yyyy/MM/dd");
var targetSheet = ss.getSheetByName(targetSheetName);
var lastRow = targetSheet.getLastRow();
var values = targetSheet.getRange(1,1,lastRow,1).getValues();
// 取得したデータから一致する日付を探す
//original i=7
for (var i=7; i<lastRow; i++){
var d = Utilities.formatDate(values[i][0],"JST", "yyyy/MM/dd");
//My if statement
if(targetDate.includes("/16")) {
var targetS = ss.getSheetByName(prevSheetName); //get the pervious sheet
var lastR = targetS.getLastRow(); //get the last row of the previous sheet
//check the values
ss.toast( "prev sheet name " + prevSheetName +"last r: " + lastR + "flag" + flag + "td " + targetDate)
//select the cell 4 of the last row
var r = prevSheetName.getRange(lastR,4);
r.setValue(flag); //set the select value
}
if(d == targetDate){
var range = targetSheet.getRange(i,4);
// データ追加
range.setValue(flag);
}
}
}
//一度に1つの日付を入力してください
}
/* 休日の保護の解除
--------------------------------------*/
function protectionRemove_(targetDate){
var ss = SpreadsheetApp.getActive();
var protections = ss.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
// 説明文が一致したら削除
if (protection.getDescription() == targetDate) {
protection.remove();
}
}
}
I was able to solve it with some of the suggestions made in the comments
I added this to my code
if(d.includes("/16") && targetDate.includes("/16")) {
var prevSheetName = String(atvc.offset(-1, -4).getValue()); //get target page
var targetSheet = ss.getSheetByName(prevSheetName);
var lr= targetSheet.getLastRow(); //select the last of the target page
var r = targetSheet.getRange(lr,4); //set the range
r.setValue(flag);
}
Please find my explanation in the comments:
function changeHoliday(ss) {
var sheet = ss.getActiveSheet(); //アクティブなシート
var sheetName = sheet.getSheetName();
var atvc = sheet.getActiveCell(); //アクティブセル
//休日シートの休日を変更した時だけ
/* Should be AND OPERATOR */
if(sheetName=='休日' && sheetName && atvc.getColumn() == 6){
var flag = atvc.getValue(); //休日かどうか
/* Should check the value */
if (flag == '休日') {
/* Row offset should be 0 */
var targetSheetName = String(atvc.offset(0, -4).getValue()); //対応するシート名
//Get previous sheet name
/* Not needed */
// var prevSheetName = String(atvc.offset(-1,-4).getValue());
/* Row offset should be 0 */
var targetDate = Utilities.formatDate(atvc.offset(0, -5).getValue(),"JST", "yyyy/MM/dd"); //対応する日付
// var targetDateEndofSheet = Utilities.formatDate(atvc.offset(0, -5).getValue(),"JST", "yyyy/MM/dd");
var targetSheet = ss.getSheetByName(targetSheetName);
var lastRow = targetSheet.getLastRow();
var values = targetSheet.getRange(1,1,lastRow,1).getValues();
// 取得したデータから一致する日付を探す
//original i=7
/* i should starts from 6 */
for (var i=6; i<lastRow; i++){
var d = Utilities.formatDate(values[i][0],"JST", "yyyy/MM/dd");
//My if statement
/* Not needed */
/*
if(targetDate.includes("/16")) {
var targetS = ss.getSheetByName(prevSheetName); //get the pervious sheet
var lastR = targetS.getLastRow(); //get the last row of the previous sheet
//check the values
ss.toast( "prev sheet name " + prevSheetName +"last r: " + lastR + "flag" + flag + "td " + targetDate)
//select the cell 4 of the last row
var r = prevSheetName.getRange(lastR,4);
r.setValue(flag); //set the select value
}
*/
if(d == targetDate){
/* Row should be i + 1 */
var range = targetSheet.getRange(i + 1,4);
// データ追加
range.setValue(flag);
/* Better to break */
break;
}
}
}
}
//一度に1つの日付を入力してください
}
To simplify:
function changeHoliday(ss) {
var sheet = ss.getActiveSheet();
var sheetName = sheet.getSheetName();
var atvc = sheet.getActiveCell();
if(sheetName=='休日' && sheetName && atvc.getColumn() == 6){
var flag = atvc.getValue();
if (flag == '休日') {
var targetSheetName = String(atvc.offset(0, -4).getValue());
var targetDate = Utilities.formatDate(atvc.offset(0, -5).getValue(),"JST", "yyyy/MM/dd"); //対応する日付
var targetSheet = ss.getSheetByName(targetSheetName);
var lastRow = targetSheet.getLastRow();
var values = targetSheet.getRange(1,1,lastRow,1).getValues();
for (var i=6; i<lastRow; i++){
var d = Utilities.formatDate(values[i][0],"JST", "yyyy/MM/dd");
if(d == targetDate){
var range = targetSheet.getRange(i + 1,4);
range.setValue(flag);
break;
}
}
}
}
}
I'm trying to copy the values of data to a new sheet. I want to copy all the information from Column AB - AZ but I want it to only copy unique values based on the values that are in column AC. For example, if the value 12345 is in column AC on Sheet1 and is also in Col C on Sheet2, then I want it to copy everything else except that row.
Here's the code I have so far
function updateSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = "Sheet9";
var destinationSheet = "Sheet10";
var source_sheet = ss.getSheetByName(sourceSheet);
var target_sheet = ss.getSheetByName(destinationSheet);
var lastCol = target_sheet.getLastColumn();
var lastRow = target_sheet.getLastRow();
//assumes headers in row 1
var r = target_sheet.getRange(2,27);
// Process sheet
_updateSpreadsheet(source_sheet, target_sheet);
}
function _updateSpreadsheet(source_sheet, target_sheet) {
var last_row = target_sheet.getLastRow();
var source_data = source_sheet.getDataRange().getValues();
var target_data = target_sheet.getDataRange().getValues();
var resultArray = [];
for (var ac in source_data) {
var keep = true;
for(var c in target_data) {
if (source_data[ac][0] == target_data[c][0]) {
keep = false; break;
}
}
Logger.log(keep);
// if(keep){ resultArray.push(source_data[ac])};
// if(keep){ resultArray.push([source_data[ac][0]])};
var columnsToKeep = [27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51];
var tempData = [];
if(keep){
for(var c in columnsToKeep){ tempData.push(source_data[ac][columnsToKeep[c]])}
resultArray.push(tempData);
}
}
last_row++;
Logger.log(resultArray);
if(resultArray.length>0){
target_sheet.getRange(last_row,2,resultArray.length,resultArray[0].length).setValues(resultArray);
}
}
It's copying the data across properly but it's failing to recognize when the values are equal to each other and therefore avoid that row. Any help would be greatly appreciated
If I understood you correctly, you want to copy the rows in which the value in column AC (Sheet1) doesn't exist in any row of column C (Sheet2).
If that's the case, you can use this:
function _updateSpreadsheet(source_sheet, target_sheet) {
var last_row = target_sheet.getLastRow();
var firstRow = 1; // First row to check
var firstCol = 28; // Column AB
var numRows = source_sheet.getLastRow() - firstRow + 1; // Number of rows to check
var numCols = 25; // Number of columns to keep
var source_data = source_sheet.getRange(firstRow, firstCol, numRows, numCols).getValues(); // Data corresponding to columns 28-52
var indexColumnToCheck = 1; // Column AC, corresponding to index 1 of source_data rows
var target_column = 2; // Column C
var target_data = target_sheet.getDataRange().getValues();
var resultArray = source_data.filter(source_row => {
return !target_data.some(target_row => target_row[target_column] === source_row[indexColumnToCheck]);
});
if (resultArray.length > 0) {
target_sheet.getRange(last_row + 1, 2, resultArray.length, resultArray[0].length).setValues(resultArray);
}
}
This function filters the source_data, checking, for each row, whether there is any row in the target_data in which column C matches column AC from source row. filter and some are used for that.
Note:
You have to enable V8 for this script to run.
Reference:
Array.prototype.filter()
Array.prototype.some()
I've been trying to get this one to work without success so far.
I need to get the TaskNumber on the first column of the row I'm on and bring it to the destination sheet, so that I can update it there.
I have the following, which I'm tweaking to achieve my goal, but I guess I've bumped into my limitation walls:
function jump() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var TaskNumberSheet = ss.getSheetByName('To-Do List');
var TaskNoRow = TaskNumberSheet.getActiveCell();
var TaskNoCol = TaskNoRow.getColumn() == 1
var TaskNo = TaskNoCol.getValue;
var sheet = ss.getSheetByName('Updates');
//var Tasks = ss.getSheetByName("To Do List").getActiveRange(Tasks.getColum(1)).getValue();
var values = sheet.getRange("B:B").getValues();
var maxIndex = values.reduce(function(maxIndex, row, index) {
return row[0] === "" ? maxIndex : index;
}, 0);
sheet.setActiveRange(sheet.getRange(maxIndex + 2,2)).setValue(TaskNo);
}
Any help is appreciate.
Cheers,
A
If I understood you correctly, you want to:
Get the value in column A from the currently active row (in sheet To-Do List).
Find the first empty cell in column B (in sheet Updates) (start looking at row #8).
Copy the value that was retrieved in step 1 to the cell retrieved in step 2.
Set the cell retrieved in step 2 as the active cell.
If that's the case, you can do the following:
function jump() {
var ss = SpreadsheetApp.getActive();
// Step 1:
var TaskNumberSheet = ss.getSheetByName('To-Do List');
var TaskNoRow = TaskNumberSheet.getActiveCell().getRow();
var TaskNoCol = 1;
var TaskNo = TaskNumberSheet.getRange(TaskNoRow, TaskNoCol).getValue();
// Step 2:
var sheet = ss.getSheetByName('Updates');
var firstRow = 8;
var column = 2;
var numRows = sheet.getLastRow() - firstRow + 1;
var values = sheet.getRange(firstRow, column, numRows).getValues().map(function(value) {
return value[0]
});
var i = 0;
for (i; i < values.length; i++) {
if (values[i] === "") break;
}
var targetRange = sheet.getRange(i + firstRow, column);
targetRange.setValue(TaskNo); // Step 3
sheet.setActiveRange(targetRange); // Step 4
}
function jump() {
var TargetRow=?;//Fillin target row
var TargetCol=?;//Fillin target column
var ss=SpreadsheetApp.getActive();
var TaskNumberSheet=ss.getSheetByName('To-Do List');
var TaskNoRow=TaskNumberSheet.getActiveCell().getRow();//Getting row from active cell
var TaskNoCol=1
var TaskNo=TaskNumberSheet.getRange(TaskNoRow,TaskNoCol).getValue();
ss.getSheetByName('Updates').getRange(targetRow,targetCol).setValue(TaskNo);
}
I have a to-do list app in google sheets. I have functions for filtering by "note type" and "done status" that can be in use at any given moment by the user.
I also have functions to easily add a new note of any given type. However, when running the function to add a new note, and the sheet is already filtered, I'm getting the following error:
"This operation is not supported on a range with a filtered out row."
Any advice on how I can add a row to a filtered range?
Here is the code that I am using to add a new note of a particular type:
function addNewCueNote() {
if( sheet.getSheetName() == sheetName ) {
var noteType = "CUE"
//ADDS ROW AND COPIES FORMULA DOWN
//SETS VARIABLES FOR LAST ROW AND LAST COLUMN
var lRow = sheet.getLastRow();
var lCol = sheet.getLastColumn();
//INSERT LAST ROW
sheet.insertRowsAfter(lRow, 1);
//COPY FORMULAS DOWN FOR SPECIFIED COLUMNS
sheet.getRange(lRow,firstCopyCol,1,numColCopy).copyTo(sheet.getRange(lRow+1,firstCopyCol,1,numColCopy));
//SETS NOTE TYPE
sheet.getRange(sheet.getLastRow(),noteTypeCol).setValue(noteType);
}
Grab the existing filter, remove it from the sheet, add the new row, then recreate the filter using the criteria from the initial filter.
function addNewCueNote() {
var sheet = SpreadsheetApp.getActiveSheet(); // added to get code to run; not sure if you handle elsewhere
if (sheet.getSheetName() === sheetName) {
// Save state of existing filter before removing it
var oldCriteria = [];
var filter = sheet.getFilter();
if (filter != null) {
var oldNumColumns = filter.getRange().getNumColumns();
for (var c = 1; c <= oldNumColumns; c++) {
var criteria = filter.getColumnFilterCriteria(c);
if (criteria != null) {
oldCriteria.push([c, criteria.copy()]);
}
}
filter.remove();
}
//*** PUT YOUR ROW INSERT LOGIC HERE ***
// Recreate filter on new data range
var dataRange = sheet.getDataRange();
var newFilter = dataRange.createFilter();
if (filter != null) {
var newNumColumns = dataRange.getNumColumns();
for (var i = 0; i < oldCriteria.length && oldCriteria[i][0] <= newNumColumns; i++) {
newFilter.setColumnFilterCriteria(oldCriteria[i][0], oldCriteria[i][1]);
}
}
}
#Nick there is something wrong with your code logic. In any ways this is a working code
// *** I have to add this for tests ***
var firstCopyCol = 3;
var numColCopy = 2;
var noteTypeCol = 2;
var sheet = SpreadsheetApp.getActiveSheet();
var sheetName = 'MatchImport';
// ************************************
function addNewCueNote() {
if (sheet.getSheetName() === sheetName) {
var filter = sheet.getFilter();
if (filter) {
var dataRange = sheet.getDataRange();
var oldNumColumns = filter.getRange().getNumColumns();
var newNumColumns = dataRange.getNumColumns();
var criterias = {};
for (var c = 1; c <= oldNumColumns && c <= newNumColumns; c++) {
var criteria = filter.getColumnFilterCriteria(c);
if (criteria) criterias['_' + c] = criteria;
}
filter.remove();
}
// START OF YOUR INSERT LOGIC
var noteType = 'CUE';
// ADDS ROW AND COPIES FORMULA DOWN
// SETS VARIABLES FOR LAST ROW AND LAST COLUMN
var lRow = sheet.getLastRow();
var lCol = sheet.getLastColumn(); // This is never used
// INSERT LAST ROW
sheet.insertRowsAfter(lRow, 1);
// COPY FORMULAS DOWN FOR SPECIFIED COLUMNS
sheet
.getRange(lRow, firstCopyCol, 1, numColCopy)
.copyTo(sheet.getRange(lRow + 1, firstCopyCol, 1, numColCopy));
// SETS NOTE TYPE
sheet.getRange(sheet.getLastRow(), noteTypeCol).setValue(noteType);
//* * END OF YOUR INSERT LOGIC
if (!filter) return;
dataRange = sheet.getDataRange();
var newFilter = dataRange.createFilter();
newNumColumns = dataRange.getNumColumns();
for (c = 1; c <= oldNumColumns && c <= newNumColumns; c++) {
if (criterias['_' + c])
newFilter.setColumnFilterCriteria(c, criterias['_' + c]);
}
}
}
Trying to send an email to a list in a Google Sheet, but only if column N gets marked "Yes." Acceptable data in column N is "Yes" or "No" so I want to test that and send emails only to the rowData that contain "yes" there. Then write the date in the adjacent column on the same row. I can't seem to figure out how to iterate through the array of objects and couldn't find any good resources to explain this. Help greatly appreciated. My best effort was emailing all the rows and then also filling in the date no matter what was in column N (Yes/No/Blank).
function sendEmails() {
validateMySpreadsheet() //a function that checks for "Yes" in column N
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheet = ss.getActiveSheet();
var dataRange = dataSheet.getRange(2, 1, dataSheet.getMaxRows() - 1, 16);
var d = new Date();
var dd = d.getDate();
var mm = d.getMonth() + 1; //Months are zero based
var yyyy = d.getFullYear();
var date = mm + "/" + dd + "/" + yyyy;
var needsaYes = "Yes";
//Gets the email template
var templateSheet = ss.getSheetByName("Template");
var emailTemplate = templateSheet.getRange("A1").getValue();
// Create one JavaScript object per row of data.
objects = getRowsData(dataSheet, dataRange);
//This is where I am stuck - how to check if column N contains a "Yes" before allowing the MailApp.SendEmail command to run.
for (var i = 0; i < objects.length; ++i) {
// Get a row object
var rowData = objects[i];
var values = dataRange.getValues();
for (var j = 0; j < values.length; ++j) {
var row = values[j];
var checkFirst = row[13]; //row J, Column N?
if (checkFirst = needsaYes) { //does column N contain "Yes"?
var emailText = fillInTemplateFromObject(emailTemplate, rowData);
var emailSubject = "mySubject";
MailApp.sendEmail(rowData.email, emailSubject, emailText);
dataSheet.getRange(2 + i, 15).setValue(date); //then write the date
SpreadsheetApp.flush();
}
}
}
}
I didn't check whole code but
if (checkFirst = needsaYes)
It may be the problem, you need to use ==
I figured it out I needed to just use the .getValues() method for my data range rather than the whole sheet and also remove the unnecessary second for loop:
var rowData = objects[i]; var checkData = ss.getActiveSheet().getDataRange().getValues();
var row = checkData[i]
var colN = row[13] if (colN == needsaYes) { //etc.