In Google Sheets how do I protect the rows in Column A which are odd from any kind of edits?
I would like to use this function
=ISODD(ROW(A1))
Protected sheets and ranges gives you this by default
Sheet1!A1
I can do this
Sheet1!A1:A1000
which will protect all 1000 rows but how do I use functions in that so I can only use ODD rows.
Here is a picture of that feature:
As mentioned in the comments on your question, I don't believe there is currently any way of manipulating range protection with Google Apps Script.
So the only option that I can think of is to manually apply the protection to 500 individual cells (in your example).
A workaround - that is not particularly tidy - is to use data validation to thwart (ultimately not prevent) entering data in even rows, with this sort of arrangement (accessed from Data, Validation...):
Savvy users who have access to the spreadsheet will be able to go in to data validation and circumvent this, though.
Google has created api for protecting sheets using google app script.
For example, to protect range A1:B10, You can run
var ss = SpreadsheetApp.getActive();
var range = ss.getRange('A1:B10');
var protection = range.protect().setDescription('Sample protected range');
If You want to protect rows alternatively, You can try something like this
function alternateRowProtection() {
var totalRows = SpreadsheetApp.getActiveRange().getNumRows();
var sheet = SpreadsheetApp.getActiveSheet();
var row = 1;
while (row < totalRows)
{
if(row%2 == 0){
sheet.getRange(row).protect().setDescription('This is protected.');
}
row++;
}
}
Related
I have little to no coding knowledge, so apologies if the solution is too obvious!
I am trying to add a Last Modified column to a Google Sheets file. To do this, I am using an AppScript function with the following code:
function setTimestamp(x) {
if(x != ""){
return new Date();
}
}
This works fine when I use setTimestamp(x) in my file. However, I am combining this with a Zapier action that creates a new row whenever new media is added. Every time a new row is created, any existing formulas are removed.
I assume I need to use ARRAYFORMULA to apply the setTimestamp formula to newly-created rows, but it must only apply to rows that aren't blank.
I have tried the following:
={"Last Modified";ARRAYFORMULA(setTimestamp(A2:A))} -> Only worked on first row
={"Last Modified";ARRAYFORMULA(B2:B=setTimestamp(A2:A))} -> Broke the file
={"Last Modified";ARRAYFORMULA(IF(A2:A)=1,setTimestamp(A2:A),"")} -> Expected 1 argument, got 3
Is there a way I can combine the IF into the script or a better way to solve the problem?
A public version of my file is available here: https://docs.google.com/spreadsheets/d/13zkVRPr2Wh5bHjCT8cenInHnBk7qkMkuEMdwUxC_cRU/edit?usp=sharing
All data is dummy data and stock photos.
Unfortunately, arrayformula does not function as an array map function for custom functions. (Even for native functions where you may expect it to work that way, it does not always, sadly.)
To handle array range, we need the custom function to handle array range directly. That also limits the number of individual calls to custom functions, which materially saves execution time.
To handle array range, there are 2 ways. I'll comment on both.
Array range directly as input of custom function
If the input is a single cell, it is read directly
If the input range spans more than a single cell, the data is read as nested lists: a list of lists of rows.
For example, A1 will be read as the data in A1. A1:B2 will be read as [[A1, B1], [A2, B2]].
You can remember it as columns of rows.
As for the input data format, numbers are taken without the display format. Texts are taken as strings.
If output is an array range, the result will automatically expend.
Thus, in your example, in B2 you can almost do
=setTimestamp(A2:A)
where setTimestamp() has been modified to
function out = setTimestamp(arr) {
out=Array(mat.length);
for (i=0;i<mat.length;i++){
j=0
if(arr[i][j] != ""){
out[i]=new Date();
}
}
return out
}
For more details, see the official help page. (Over the years, more details have become available.)
Almost, but not quite. For your direct question, above provides the answer. However, you seem to have an implicit requirement that your custom function is executed every time a new URL is found. Be careful that what happens here is that every time Google Sheet updates cell content, a new Date() is created and outputted.
Array range read within custom function
Since you know your URLs are in A2:A, and you want the output of your custom function to be B2:B, you can read and modify those ranges directly within your custom function via the Range Class.
In this route, you may find getLastRow(), getLastColumn() in Sheet and getNextDataCell() in Range convenient.
When you need to execute your custom function, you can run it manually or add onEdit() trigger to your custom function. (But onEdit() itself can mean substantial UI lag when using the sheet. It's usually more appropriate for sheets that parse external data automatically. See other triggers in the link for motions.)
In your example, you can almost do
function setTimestamp() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var lastRow = sheet.getLastRow();
var row=1;
var cell = sheet.getRange(row,1).getValue();
while (row<=lastRow) {
if(cell.getValue() != ""){
sheet.getRange(row,2).setValue(new Date());
}
cell = sheet.getRange(row,1,lastRow).getNextDataCell(SpreadsheetApp.Direction.DOWN);
row=cell.getRow();
}
}
which will scan for all URLs in A2:A and write current time to B2:B when executed.
Again, your example implicitly points to updating only when a new URL is found. So be careful about that. Use triggers as needed.
As for the need to place formula in B1, you can (and should) reference the output of your other application in a different sheet so that you or a different application of yours can edit without conflict.
Thus, for what was asked, we have everything.
I'm using arrayformulas to populate certain columns of my sheet, depending on values in other sheets. Occasionally users accidentally will put some value in these columns, and this will stop arrayformula from working. I want to protect these columns, but still allow adding/editing/deleting rows.
Consider this example spreadsheet: I want Id row to be protected, but allow addition/deletion of rows.
https://docs.google.com/spreadsheets/d/1Dnj7OE5XZL09gllHVwPgv-5GRoM-lxVCxTCI_-kURdM/edit#gid=0
Is this possible at all with Google Sheets?
You can't directly disable input but you can use Data Validation instead
By going to Data > Data Validation and filling it with the following:
Cell range: YourSheet!C2:C
Criteria: Custom formula is - =C2:C = A2:A & "["&B2:B&"]"
On invalid data: Reject input
Appeareance: Optional message
Once you've done this, try to fill some cell in the C column and you'll see a message: There was a problem - Your optional message
As a different approach you can use Apps Script Simple Triggers
By going to Tools > Script Editor and copying the following code:
function onEdit(e) {
var column = e.range.getColumn();
var sheet = SpreadsheetApp.getActiveSheet();
if (column === 3) {
e.range.setValue("");
}
}
Which is more like an "undo" function.
References
Simple Triggers
Event Objects > onEdit
rows could be added by brute force with this formula:
=ARRAYFORMULA(ROW(INDIRECT("1:"&ROWS(A:A)+1)))
but escalation cant be controlled
I'm new to this,
I have 2 google spreadsheets:
Spreadsheet A: The active sheet Containing multiple tabs with information to be Pushed to B.
Spreadsheet B: A spreadsheet with a single tab. The same headers and structure as spreadsheet A.
Based on the user selecting the answer "Yes" in the first column of any of the 1 tabs in Spreadsheet A, I would like that entire row to move over to Spreadsheet B.
I have modified a script that works on a single spreadsheet (ie moving rows from tab to tab) to attempt to get it to work between spreadsheets:
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var tss = SpreadsheetApp.openById('B').getSheetByName('Sheet 1');
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if(r.getColumn() == 1 && r.getValue() == "Yes") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var target = tss.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).moveTo(target);
}
}
Probably needless to say, this yields no result. Having searched through a number of posts and forums I only see individuals posting about how to move rows between tabs but not between entirely separate spreadsheets. Is it even possible to do this? If so, what am I doing wrong in the script?
Thank you so much for anyone who takes the time to assist.
Anthony
Following a dialogue with the OP in the comments section, it was indicated that the original spreadsheet did not to be kept secret.
Consequently, the desired functionality can be provided by using a combination of IMPORTRANGE() and QUERY() in the spreadsheet with no need to use Google App Script. For instance,
=QUERY(IMPORTRANGE(url,range),"select A where B matches 'Yes'") or similar
This imports data from a second spreadsheet and then the QUERY() function acts as a way of filtering the imported range by certain criteria.
Once the imported range is authorised, the editors of the spreadsheet can access it by, e.g. removing or modifying the query. You could prevent this by protecting that particular cell, if needed.
I have tickets I'm selling for an event, and I have a google spreadsheet with a list of email addresses that should get a discount.
I'd like to check the google sheet once a user types in their email address in the form, and then if theirs is in our list, the discount gets applied.
I can't seem to figure out how to do this. The example code here: https://developers.google.com/sheets/api/quickstart/js requires users to allow access to their google drive, which isn't what I need. I just need to check my own spreadsheet.
How might I do this?
You can use Apps Script (since you're using Forms), if you want a handy solution. I don't know the complexity of your usecase but I'll just demo that this is doable:
So this is my spreadsheet, you can see the names on Column A:
name(0,0)
floyd(1,0)
conor(2,0)
john
carmack
borja
adam
I'm going to look for "adam"
function findPerson() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getSheets()[0].getRange("A1:A7").getValues();
for(var i = 0; i < range.length ; i++){
Logger.log(range[i]);
if( range[i] == "adam"){
Logger.log("FOUND YAH!");
}
}
}
And there, I found him! ;)
If that's a Google Form you're using, the next step would be learning how to Connect Spreadsheet to Google Forms
I have a fairly large Google Sheets document that I need to set up to count how many rows there are based on a certain color.
In my document I am cross-comparing files found in two different folders on our companies servers. One folder (column 1) has nearly 800 individual files and the second folder (column 2) has around 250/300 individual files. When a file is the same between the two folders I will highlight the row (for the sake of argument) Orange. I want to set up google sheets to count how many rows have the orange color.
After researching it I found a way that you can set that up via VBA in Excel, but my document needs to be accessible to the rest of the team (via google drive) Is there a way to set up Google Sheets to do that?
It depends on what you want it returned. Do you just want a number? Do you want a list of only "colored" rows? etc...
One method would be to use Appscript. In your Spreadsheet go to Tools -> Script Editor and create a new script:
//Get Sheet, Range, and Colors
var count = 0;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range = sheet.getDataRange();
var bgColors = range.getBackgrounds();
//Loops through ROWS to get # of X color
for (var i in bgColors) {
if(bgColors[i][0] == "INSERT COLOR CODE HERE")
count++;
}
//Do something with your count
ss.getRange("A1").setValue(count);
If you don't want to have to repeatedly call the function everytime, I suggest you put it in a trigger like onOpen(). If you need to do something using the data within the cells, it's similar, just rather than incrementing a count variable, get the values in the specific range and do whatever calculations.