Clear cell value of adjacent cell if value is a date - javascript

I've been working on this script in Google Apps Script for a little while now and think I've got it almost figured out. I could really use another pair of eyes though, as it hasn't yet worked to completion.
What I'm trying to do:
I'm working on a sheet dealing with tentative and actual dates. These two dates are in adjacent columns. Once the "actual" date gets filled into its cell, I would like the "tentative" date to be deleted.
Here is my script thus far:
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range = sheet.getRange('F4:F');
var range2 = sheet.getRange('E4:E');
var rule = range.getDataValidation();
if (rule != null) {
var criteria = rule.getCriteriaType();
var args = rule.getCriteriaValues();
var clear = range2.clear({validationsOnly: false});
}
}
What do I need to do to get this script running?

It appears you want to delete the value in column E if column F was filled. If so, I don't see the need for dealing with data validation rules. The following script checks that the edited cell is in column F, row >= 4, and that the new value in the cell is a date. If these conditions hold, it clears the cell to the left (using offset).
function onEdit(e) {
if (e.range.getColumn() == 6 && e.range.getRow() >= 4) {
if (e.range.getValue() instanceof Date) {
e.range.offset(0, -1).clear();
}
}
}
Note: this script uses the event object passed to onEdit function. Interestingly, its "value" property turned out to be not useful here, because it is not a date object even when a user enters a date. This is what I use e.range.getValue() instead.

Related

Google Sheet - Add Timestamp When Any Cell on Specific Sheet is Modified

I have a Google Sheet file that has many tabs (sheets) in it. What I am looking to do is place a timestamp when any cell on a specific page is updated or modified.
Sheet name = "Data-Dashboard"
Cell to place timestamp on "Data-Dashboard" is B1.
I wonder if having the timestamp on the sheet that is updating the timestamp on edit if it will create a loop so then I can exclude row 1 and just monitor the remaining rows and columns.
Here is my starting point:
function onEdit(e) {
var col = e.range.getColumn();
if(col == 1, 2, 3, 4, 5, 6) {
e.source.getActiveSheet().getRange(1,2).setValue(new Date())
That puts the timestamp in the correct place and works when I modify anything in rows A-F. But it applies to all sheets, I need to limit it to a single sheet. Any help would be appreciated!
From the question
What I am looking to do is place a timestamp when any cell on a specific page is updated or modified.
Sheet name = "Data-Dashboard"
Cell to place timestamp on "Data-Dashboard" is B1.
The onEdit trigger is triggered when any edit is done through the Google Sheets UI, to limit the writing of the timestamp to only do it when the certain sheet is edited, then you should add a conditional statement, i.e. an if statement.
function onEdit(e){
/* Using a conditional operator to assign the active sheet
when this function is ran without providing an event object, i.e.
when running from the script editor */
const sheet = e ? e.range.getSheet() : SpreadsheetApp.getActiveSheet();
if(sheet.getName() === 'Data-Dashboard'){
sheet.getRange('B1').setValue(new Date();
}
}
Regarding your starting point and that it's working when columns A-F are edited, it's worthy to note while all the parts are evaluated, only the last part is considered as the condition of the if statement, in this case 6, which is a truthy value, meaning that always will be true. The following version includes the condition to log the timestamp only when columns A to F from Data-Dashboard sheets are edited.
function onEdit(e){
/* Using a conditional operator to assign the active sheet
when this function is ran without providing an event object, i.e.
when running from the script editor */
const sheet = e ? e.range.getSheet() : SpreadsheetApp.getActiveSheet();
if(sheet.getName() === 'Data-Dashboard' && e.range.columnStart <= 6){
sheet.getRange('B1').setValue(new Date();
}
}
References
comma operator
Related
How do I restrict a trigger to a specific sheet/tab?
Try this:
function onEdit(e) {
const sh = e.range.getSheet();
var col = e.range.getColumn();
if (sh.getName() == "Your Sheet Name" && e.range.columnStart < 7 && e.range.rowStart > 1) {
sh.getRange(1, 2).setValue(new Date());
}
}

How to check if a checkbox is checked, and change data in another cell based on it, repeated for multiple rows

I'm trying to make a script that, whenever triggered, runs through each row checking if the checkboxes in column F are checked. If so, the content of column A in that row is replaced with a 0. Upon running the code, nothing happens. I am not sure where the script gets caught up, but I would assume it's in the for() loop. I have also included a picture of the spreadsheet itself for easier understanding. Any help is appreciated.
My code:
function onEditButAgain(event){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var editedCell = sheet.getActiveCell();
var columnToSortBy = 6;
if(editedCell.getColumn() == columnToSortBy)
{
for(var x = 2; x<=100; x++)
{
var checked = sheet.getRange(x, 6);
if(checked.isChecked == true)
{
var date = sheet.getRange(x, 1);
date.setValue("0");
}
}
}
}
The image of the spreadsheet itself:
Explanation:
Your goal is to manually execute a script that will change the value in column A whenever the cell in the same row in column F is checked.
The obvious issue in the code is isChecked which should be isChecked(). You use parenthesis when you want to execute a function and in this case you want to execute isChecked.
In this case, an onEdit script will be redundant. If you want to use an onEdit trigger with the name onEditButAgain you have to create an installable trigger. But you don't need an installable trigger for this purpose.
Although you can wrap up the following code in this answer in a simple onEdit() function, the event object is not used and the code won't be 100% efficient. But if you want to run it manually, you don't need a trigger anyway.
When you use a loop, it is not a good practice to use methods like getRange or isChecked or setValue because these methods are computationally expensive and your script will very slow. Work with arrays instead and getValues/setValues.
Solution:
function myFunction(){
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet1'); // change it to your sheet name
const valA = sh.getRange('A2:A'+sh.getLastRow()).getValues().flat();
const cb = sh.getRange('F2:F'+sh.getLastRow()).getValues().flat();
const new_valA = cb.map((c,i)=> [c ? "0" : valA[i]]);
sh.getRange(2,1,new_valA.length,new_valA[0].length).setValues(new_valA);
}

Automatically protect a cell / range after inputting data in Google Sheets

I have a public spreadsheet, where people can enter their names under the column "Name". Everything in this sheet is protected, except for the cells in the column "Name". Since the spreadsheet is public, I want to avoid a situation where someone can troll and delete all the names that have been inputted. Hence, I'm trying to set up a script using the on edit triggers to protect the cell in this range after anyone has entered their name in a cell. So far I've been manually protecting the cells after a name has been entered.
I've found out that the best way to do this would be to use the on edit trigger. I have used javascript before but as I'm new to google spreadsheet scrips, I can't get my script to run like it's supposed to. The current script is supposed to automatically protect the range on edit, and add a description of when the protection was done.
Sample spreadsheet with the script in it here: https://docs.google.com/spreadsheets/d/18NlVKcaeyOkgqIa6WAuDsu5TSYK37m_vXLmp7p7Q8kc/edit?usp=sharing
function protectOnEdit(event) {
var ss = SpreadsheetApp.getActive();
var range = ss.getRange('Sheet1!A2:A1000');
var timeZone = Session.getScriptTimeZone();
var stringDate = Utilities.formatDate(new Date(), timeZone, 'dd/MM/yy HH:mm');
var description = 'Protected on ' + stringDate;
var protection = range.protect().setDescription(description);
// below code taken directly from Google's documentation
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
}
Reference: https://developers.google.com/apps-script/reference/spreadsheet/range#protect()
The data range in question is A2:A1000 and currently it -seems- to partially work, however, it protects the WHOLE range after editing a single cell, instead of just protecting the edited cell like it's supposed to.
Are there any steps I'm missing in order for the script to lock the cells individually, instead of the whole range? Any insights are very appreciated!
I have made some corrections:
function protectOnEdit(e) {
var range = e.range;
// Be sure to have edited the sheet "Sheet1"
if (range.getSheet().getName() != "Sheet1") return;
// Be sure to have a single cell as edited range
if (range.getWidth() != 1 || range.getHeight() != 1) return;
// Be sure to have edited cell inside A2:A1000
if (range.getColumn() != 1 || range.getRow() < 2) return;
// Be sure to have non-blank new value
if (!e.value) return;
// Protect this range
var timeZone = Session.getScriptTimeZone();
var stringDate = Utilities.formatDate(new Date(), timeZone, 'dd/MM/yy HH:mm');
var description = 'Protected on ' + stringDate;
var protection = range.protect().setDescription(description);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) protection.setDomainEdit(false);
}
At first the script checks several conditions for desired protection. They seem to me to be important for the task. If all of them are TRUE, then protects a single cell. The spreadsheet owner has no restrictions to edit protected cells but other editors have.
Of course, the owner should install a trigger for this function properly to automate the process.

Using .getValue() in a loop. This results in many calls. Is there a simpler way to do this?

Sorry if this is a bit newbie.
What I'm trying to do is take a column and delete the values in some of its cells if they do not contain a formula.
function clean() {
var ss = SpreadsheetApp.openById("KEY_REMOVED_FOR_OBVIOUS_REASONS");
var sheet = ss.getSheetByName("TEST");
var limit = sheet.getMaxRows();
for(var i = 6; i < limit; ++i){ // Declares variable "i", which represents the row we are currently checking. Basically says "if our row isn't the last one, execute this block then add to i".
var range = sheet.getRange(i,2);
if (range.getValue() != "") { // Leverages the fact that .getValue() cannot return functions. If our range is blank, execute this block.
range.clear();
}
}
}
I established a loop which checks every row one by one. What I'm wondering is, is there a more efficient way to do this than having to call the server every time I do the loop?
getValues() exists of course, but I'm not sure how one might interact with only the desired cells returned by that.
I did some testing. .getValues() will return the computed values if there are formulas.
The following code works for me.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getLastRow();
var range = sheet.getRange(1,1, rows);
var values = range.getFormulas();
range.clear();
range.setFormulas(values);
}
Short answer
Regarding the too many calls issue, use getLastRow() instead of getMaxRows() and use the returned value (number of rows) to get the required range in just one call.
Explanation
getLastRow() returns the position of the last row that has content while getMaxRows() returns the maximum height of a sheet regardless of content.
getRange(row, column, numRows) returns in just one call the complete range.

Creating a null value in a DATE validated column

I have a uiApp that is creating table views from spreadsheet data, but I'm having difficulty with empty cells in a date column.
Basically, some of the spreadsheet cells are optional, and I haven't figured out how to put no (or a null) value into the table. Right now it works if I set the column value to:
projectDataRaw[i][projectDataKeys[j]]= new Date(); // inserting dummy data
But I want to leave those cells blank in the table view too, however, when I use:
projectDataRaw[i][projectDataKeys[j]]= null; //
Or some variation of that, it errors because the column type is set to DATE.
I could convert to strings, but I kind of wanted to be able to compare date values to each other when they are available.
-- Example of issue with table view and null data --
// load data from sheet
var _PMsheet = "sheet key";
var sSheet = SpreadsheetApp.openById(_PMsheet);
var projectData = sSheet.getRangeByName("ProjectDateCol").getValues();
var projectDataTable = Charts.newDataTable();
projectDataTable.addColumn(Charts.ColumnType.DATE, "Date Column");
for (var i=0; i < projectData.length; i++) {
if (projectData[i] == undefined || projectData[i] == "undefined") {
projectDataTable.addRow([null]);// Object type does not match column type.
// null fails, "undefined" fails, "" fails, [] fails
}
else {
projectDataTable.addRow([projectData[i]]); // this cell has a valid date obj
}
}
I created a simple script which writes null to the A1 cell and current date to the A2 cell. The complete column A is validated to be a date time. After the script execution the cell A1 is empty even if it contained a value before and there is no any warning or error message.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var rangeValues = [[null], [new Date()]];
sheet.getRange("A1:A2").setValues(rangeValues);
}
If this example does not help you, please publish as smallest as possible working script which does not work as you expected.
Update, basing on the author clarification: I wrote a simple script trying to find the problem cause and to find a workaround. The code is bellow. If to uncomment the data.addRow(['', 2.0]); line, then an exception thrown by the addRow method. It is an expected behavior. If to uncomment the data.addRow([null, 3.0]); line, then the chart.setDataTable(data) line throws an exception. This behavior is not correct, from my point of view, and I think it is necessary to report an issue to the issue tracker.
Also there is a workaround of this problem by using the DataTableBuilder.setValue method. The code demonstrates it.
By the way, the DataTableBuilder.setValue description in the documentation is not correct. It does not contain the column parameter. It is a topic to the issue tracker.
function doGet(e) {
var app = UiApp.createApplication();
var chart = Charts.newTableChart();
var data = Charts.newDataTable();
data.addColumn(Charts.ColumnType.DATE, 'Date');
data.addColumn(Charts.ColumnType.NUMBER, 'Value');
data.addRow([new Date(), 1.0]);
// data.addRow(['', 2.0]); // throws the "Object type does not match column type" exception in this line.
// data.addRow([null, 3.0]); // throws the "We're sorry, a server error occurred. Please wait a bit and try again" exception in the "chart.setDataTable(data)" line.
data.setValue(1, 1, 3.0);
chart.setDataTable(data);
app.add(chart.build());
return app;
}

Categories