I have an Array of Ranges that I am trying to loop through to protect, but it is not working anymore. I do not know why. It was working yesterday just fine, but the protections are not happening on the sheet. I have tried removing all editors, but we are still able to edit. Basically, I have a sheet of Named ranges; I create an array based on those ranges; then get a rangelist from that array; I then loop through the rangelist to protect each range.
function protectTU(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s1 = ss.getSheetByName("Named Ranges");
var s2 = ss.getSheetByName("Copy of TRUCK UPDATES");
var lr1 = s1.getRange("A3:A").getValues(); //Find last rowv
var lr2 = lr1.filter(String).length; //Find last row
var lr3 = parseInt(lr2) - 1;
var data1 = s1.getRange("A3:E" + lr2).getValues();
var dis = 2;
var i = 1;
var v1 = [];
for (var j = i; j < lr3 - 1; j++)
{
if(data1[j][4] == dis)
{
v1.push(data1[j][1]);
}
if(dis == 1 && j == 43)
{break;}
if(dis == 2 && j == 81)
{break;}
if(dis == 3 && j == 135)
{break;}
}
var ranges = s2.getRangeList(v1);
**for (i = 0; i < ranges.length; i++)
{
var protection = ranges[i].protect().setDescription('Please see the office for permission');
}**
}
Try this:
I would like to see an image of your spreadsheet and some data to confirm so this is pretty much a guess.
function protectTU(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s1 = ss.getSheetByName("Named Ranges");
var s2 = ss.getSheetByName("Copy of TRUCK UPDATES");
var lr3 = s1.getRange(3,1,s1.getLastRow()-2,1).getValues();//better not to use A1Notation for setting up range here
var data1 = s1.getRange(3,1,s1.getLastRow()-2,5).getValues();//and here
var dis = 2;
var i = 1;
var v1 = [];
for (var j=0;j<data1.length;j++) {
if(data1[j][4] == dis) {
v1.push(data1[j][1]);//so hopefully column2 has A1Notation
}
if(dis==1 && j==43){break;}
if(dis==2 && j==81){break;}
if(dis==3 && j==135){break;}
}
var ranges = s2.getRangeList(v1).getRanges();
//I don't use protection much so I don't know about this
for (i = 0; i < ranges.length; i++){
var protection = ranges[i].protect().setDescription('Please see the office for permission');
}
}
Here is a link to a basic version of the spreadsheet. The answer above helped a lot by the way, but it is still too slow. The answer was what I needed. Now I have to fix that. So my people are divided by truck numbers. However, there is one person that has random numbers. I cannot lock a huge chunk at time because another editor needs to edit their trucks too.
So I need to Lock the rows for trucks 301/302/303/305/310.. while not locking the numbers in between them which is why I have to loop it. I need to be able to lock out one person at a time. The divisions are 1:301-350, 2:351-394, 3:395-335..., 4:Random numbers.
https://docs.google.com/spreadsheets/d/1HJROXH1fhkSMtdgQQqUUrKxi0hj7s42I9rjCoz-Vswc/edit?usp=sharing
Actually, it was s2.getRangeList(v1).GETRANGES() part.
So, everyone views the same spreadsheet, but if I need for x person to see me urgently I want to be able to freeze what they are doing until they see me. Additionally everyone can keep working on their portion of the spreadsheet.
Related
I'm trying to create an apps script to autofill a gantt chart when the sheet is edited, but having trouble.
Here is a link to the spreadsheet if it helps.
function ganttChart()
{
const ss = SpreadsheetApp.getActiveSpreadsheet();
const ganttSheet = ss.getSheetByName("Gantt Chart");
var headerRow = ss.ganttSheet.getRange('headerRow').getRow();
var lastRow = ss.ganttSheet.getLastRow();
var lastCol = ss.ganttSheet.getLastColumn();
var firstTask = headerRow + 1
var taskRoleCol = ss.ganttSheet.getRange('taskRole').getColumn();
//I'm not sure if I need to do the below RoleCol if I already have a named range -- this will return an integer which is the column #
var roleCol = ss.getSheetByName("Roles").getRange('Roles').getColumn();
var taskCol = ss.ganttSheet.getRange('taskNames').getColumn();
var startWeekRow = ss.ganttSheet.getRange('startWeek').getRow();
var expDurationCol = ss.ganttSheet.getRange('expDuration').getColumn();
//set the requirements for the edit trigger -- not sure what these would be
//if (e.range)
//{
for (var i = firstTask; i < lastRow; i++)
{
var currentTask = ss.ganttSheet.getRange(i, taskCol).getValue();
var currentStartWeek = ss.ganttSheet.getRange(i, startWeekCol).getValue();
var currentTaskExpDuration = ss.ganttSheet.getRange(i,expDurationCol).getValue();
var currentTaskRole = ss.ganttSheet.getRange(i,taskRoleCol).getValue();
if (currentTask != null)
{
if (currentStartWeek != null)
{
//for loop to identify the column that matches the start week #
for (var j = 0; j < lastCol; j++)
{
var checkWeek = ss.ganttSheet.getRange(startWeekRow, j).getValue();
if (checkWeek == currentStartWeek)
{
//identify the range
var taskTimeRange = ss.ganttSheet.getRange(i,j - 1,(currentTaskExpDuration*2 +1), 1);
//for loop get the background color based on role
for (var k = 0; k < lastRow; k++)
{
var checkRole = ss.ganttSheet.getRange(k, roleCol).getValue();
//if role value matches the currentTaskRole
if (checkRole == currentTaskRole)
{
var roleColor = ss.ganttSheet.getRange(k, roleCol).getBackground();
//reformat the range based on duration
taskTimeRange.setBackground(roleColor);
}
}
}
}
}
}
}
//}
}
I took off the "onEdit" to try and get the program to work on run, but I'm still getting a "cannot read properties of undefined" error.
What should happen is:
when a user edits the "Gantt Chart" sheet
the program changes the background color in the corresponding range to indicate the weeks a task is being worked on, based on the start week and calculated duration
the background color should correspond to the task role, based on the colors set in the "Roles" sheet
If the above isn't clear, here is a link to a video where I try to explain what the program should do
The question relates to onEdit but the trigger is, at this point of development of the script, irrelevant, since the script is littered with syntax errors.
Even so, I suggest that the trigger is irrelevant in any event. The script can/should be triggered when the data has been populated. This trigger can be done manually, or (perhaps) via a menu option.
getRange(), getLastRow() and getLastColumn are sheet-based methods.
Incorrect
var headerRow = ss.ganttSheet.getRange('headerRow').getRow()
var lastRow = ss.ganttSheet.getLastRow()
var lastCol = ss.ganttSheet.getLastColumn()
Correct
var headerRow = ganttSheet.getRange('headerRow').getRow()
var lastRow = ganttSheet.getLastRow()
var lastCol = ganttSheet.getLastColumn()
Other
startWeekCol is not defined
for (var j = 0; j < lastCol; j++)
"j" substitutes for the column number, but a value of 0 is invalid
var taskTimeRange = ganttSheet.getRange(i,j - 1,(currentTaskExpDuration*2 +1), 1)
when "j" is 1, "j-1" resolves to 0 (zero) which is invalid
for (var k = 0; k < lastRow; k++)
"k" substitutes for the row number, but a value of 0 is invalid
I am creating a google sheet which contains fixtures and I would like to color all the cells featuring the fixtures which show how relatively easy/difficult the match is for a given team. This is my spreadsheet. The following is the code that I am currently using -
// Function to find the row number of a given fixture
function findRow(empName){
var ss=SpreadsheetApp.getActive();
//var fix = ss.getSheetByName('Fixtures');
var fdr = ss.getSheetByName('FDR');
var data = fdr.getDataRange().getValues();
for(var i = 0; i<data.length;i++){
if(data[i][1] == empName){ //[1] because column E
Logger.log((i+1))
return i+1;
}
}
}
// Function to color the given cell based on its fixture difficulty
function colorMeUp() {
var ss = SpreadsheetApp.getActive();
var fix = ss.getSheetByName('Fixtures');
var fdr = ss.getSheetByName('FDR');
for(var i = 4; i <= 23; i = i + 1){
for(var j = 3; j <= 40; j = j + 1){
var temp = fix.getRange(i,j).getValue(); // Find the fixture to color
var master = fix.getRange(i,2).getValue(); // Find the reference team
var rowNumMaster = findRow(master);
var rowNumTemp = findRow(temp);
if(rowNumTemp < 23){
rowNumMaster = rowNumMaster + 20;
}
var tempRating = fdr.getRange(rowNumTemp,4).getValue();
var masterRating = fdr.getRange(rowNumMaster,4).getValue();
var fixDiff = masterRating + (6-tempRating); // Calculate relative fixture difficulty
var rang = fdr.getRange(1+fixDiff,8).getBackground();
fix.getRange(i,j).setBackground(rang);
}
}
}
As you can see, this code uses the table given in the "FDR" sheet to assign a colour to each fixture given in the "Fixture" sheet. However, when I press the ugly blue button on the left side the process of it happening is painfully slow and it exceeded the maximum time. Is there any way to make this process go faster by tweaking this code/ using a different approach? I am very new to all this so don't really know what I can do to make this better/faster.
Try integrating if(data[i][1] == empName){ column two of data into a flatten array and then you can use indexOf instead of call a function twice in the center of a loop. So inotherword trash findRow and build it inside of colorMeUp
So something like this:
function colorMeUp() {
var ss = SpreadsheetApp.getActive();
var fix = ss.getSheetByName('Fixtures');
var fdr = ss.getSheetByName('FDR');
const sA = fdr.getRange(1,2,fdr.getLastRow()).getDisplayValues().flat();
for(var i = 4; i <= 23; i = i + 1){
for(var j = 3; j <= 40; j = j + 1){
var temp = fix.getRange(i,j).getValue(); // Find the fixture to color
var master = fix.getRange(i,2).getValue(); // Find the reference team
var rowNumMaster = sA.indexOf(master) + 1
var rowNumTemp = sA.indexOf(temp) + 1
if(rowNumTemp < 23){
rowNumMaster = rowNumMaster + 20;
}
var tempRating = fdr.getRange(rowNumTemp,4).getValue();
var masterRating = fdr.getRange(rowNumMaster,4).getValue();
var fixDiff = masterRating + (6-tempRating); // Calculate relative fixture difficulty
var rang = fdr.getRange(1+fixDiff,8).getBackground();
fix.getRange(i,j).setBackground(rang);
}
}
}
I threw this together rather quickly so it may not actually work but it should speed things up considerably using that approach. If you've done a few web apps then my guess is that you're a reasonable coder and should be able to integrate this into you code. Or it's possible that I'm totally FOS.
It would be really good to get rid of the getValue() in the loop in lieu of getValues() outside of the loop and use array indices to access the same data . Same with getbackground. I'd need to see your data to figure that out.
BTW I don't follow links to spreadsheets so giving me access to data involves post tables.'
This is the script I have currently. I just picked up script editing recently and I cant resolve this issue.
I am trying to delete the same row in 2 sheets.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Verification');
var r = s.getRange('Q:Q');
var v = r.getValues();
for(var i=v.length-1;i>=0;i--)
if(v[0,i] == 'Cancelled')
s.deleteRow(i+1);
ss.getSheetByName('S4 Branch');
s.deleteRow(i+1);
I believe your goal as follows.
You want to check the values of the column "Q" of "Verification" sheet.
When the value is Cancelled, you want to delete the row, and also, you want to delete the row of the same row number in "S4 Branch" sheet.
Modification points:
If you want to compare the value of Cancelled with v, please modify if(v[0,i] == 'Cancelled') to if(v[i][0] == 'Cancelled').
When var r = s.getRange('Q1:Q' + s.getLastRow()); is used as the data range instead of var r = s.getRange('Q:Q');, the process cost will be able to be reduced a little.
In your script, the if statement and the for loop are not enclosed by { and }. By this, each script is used for just under the line. For example, for(var i=v.length-1;i>=0;i--) is used for if(v[0,i] == 'Cancelled'). And, if(v[0,i] == 'Cancelled') is used for s.deleteRow(i+1);. By this, i at s.deleteRow(i+1); is always -1. So s.deleteRow(0); is run. In this case, an error occurs. I think that this might be the reason of your issue.
When above points are reflected to your script, it becomes as follows.
Modified script:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Verification');
var r = s.getRange('Q1:Q' + s.getLastRow());
var v = r.getValues();
var s2 = ss.getSheetByName("S4 Branch");
for (var i = v.length - 1; i >= 0; i--) {
if (v[i][0] == 'Cancelled') {
s.deleteRow(i + 1);
s2.deleteRow(i + 1);
}
}
}
References:
getLastRow()
deleteRow(rowPosition)
If you want to try the below script:
You can add more sheets by updating the sheetArr list and it can auto-trigger on Edit.
function onEdit() {
deleteRow();
}
function deleteRow() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetArr = ['Verification', 'S4 Branch'];
for (var i = 0; i<sheetArr.length; i++){
var lr = ss.getSheetByName(sheetArr[i]).getDataRange().getLastRow();
var s = ss.getSheetByName(sheetArr[i]).getRange('Q1:Q' + lr ).getValues();
for (var j = s.length -1; j >= 0; j--){
if (s[j] == 'Cancelled'){
delrow = ss.getSheetByName(sheetArr[i]).deleteRow(j+1);
}
}
}
}
On my column D there are two values - exp and ok; I am meant to remove all exp rows but there are sometimes 1000's and the macro takes ages to complete.
So from the normal macro - deleting 1 row at a time, I'm trying to delete multimple rows.
function expirate_removed(){
var app = SpreadsheetApp.getActive();
var activesheet = app.getActiveSheet()
var r = activesheet.getRange('D:D');
var v = r.getValues();
for(var i=v.length-1;i>=0;i--)
if( v[0,i-50]=='exp')
activesheet.deleteRows(i-50, 50);
};
I have previously recorded a macro sorting the sheet, making sure all the exp ones are at the bottom, thinking this would help me save a few good minute.
Problem is, whenever I run the macro it deltes one set of 50' rows and then it gives an error saying the rows are out of bounds..
Any suggestions? ( I already have a working version for deleting one row at a time)
Assuming the column D is sorted, try this:
function expirate_removed(){
var app = SpreadsheetApp.getActive();
var activesheet = app.getActiveSheet();
var r = activesheet.getRange('D:D');
var v = r.getValues();
var startRow = 0;
var endRow = 0;
for(var i=0; i<v.length; i++) {
if(v[i] == 'ok'){
startRow = i+1;
break;
}
}
for(var i=startRow; i<v.length; i++) {
if(v[i] != 'ok'){
endRow = i;
break;
}
}
activesheet.deleteRows(startRow, endRow - startRow + 1);
};
Maybe my understanding is wrong but you could use the mapping which helps you to get faster.
function expirate_removed(){
var app = SpreadsheetApp.getActive();
var activesheet = app.getActiveSheet();
var values = activesheet.getRange(1,5,activesheet.getLastRow()).getValues();
var mapValues = values.map(function(r){return r[0]});
mapValues.forEach(function(elt,index){
if(elt == "exp"){
activesheet.deleteRow(index);
}
});
I use a cell to help trigger functions in a spreadsheet I made
ie: when cell A13 = "loading old orders" do this
It should run for around 4 mins then end
then it repeats this until the code finds "2015-11-09T22:40:56" in col C
I am finding that some of these calls are taking a lot longer now that the sheet is growing in size.
I am after some ideas to make the code more efficient
Thanks in advance
function getOldOrders(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var status = ss.getSheetByName('Main');
var sheet = ss.getSheetByName('Summary');
var StatusCellToWatch = status.getRange("A13");
var offset = 0;
if (StatusCellToWatch.getValue() === "Loading Old Orders") {
try {
var beforedate = sheet.getRange(sheet.getLastRow() - 1,21).getValue();
}
catch(e) {
var beforedate = "2999-03-22T19:28:02";
}
//StatusCellToWatch.setValue(beforedate);
getOrders('&before='+beforedate,offset);
StatusCellToWatch.setValue('Loading Old Orders');
//Set sheet status so that the dataset just updates and grabs new orders
var data = sheet.getRange('C30000:C').getValues();
var search = "2015-11-09T22:40:56";
for (var i=0; i < data.length; i++) {
if (data[i][0] == search) {
StatusCellToWatch.setValue('');
FormatSummary();
}
}