I have a Google Sheet file linked to a Google Form, that I use to record registrations for classes by different teachers. Inside of it I create extra columns that mark "X" in a row, when someone registers for that particular class, using an =IF function. I'd like those "X"'s to be checkboxes so that I can check attendees in the sheet itself as they arrive, but there is no method I can find for a function to output a tickbox into the cell and I have next to no knowledge of JavaScript.
Example Sheet: https://docs.google.com/spreadsheets/d/19BUkEfo8dWcAfPDhmhBTuhC1-V-QROlfF0pWH75c9VA/edit?usp=sharing
Ideally, I'd have a custom function that basically does the same as my =IF but inserts a checkbox instead of writing "X" (ie. if cell A contains text from cell B insert checkbox, else leave empty).
Alternatively I also found this solution to a similar problem, that I think would work, but I don't know enough to tweak it to my needs. This way I'd keep my sheet as is and the script would just replace the X's with checkboxes?
Probably it is not very efficient but it works:
// function creates menu 'SCRIPTS'
function onOpen() {
SpreadsheetApp.getUi().createMenu('SCRIPTS')
.addItem('Insert checkboxes', 'replace_x_to_checkboxes')
.addToUi();
}
// function replaces all 'X' on the sheet with checkboxes
function replace_x_to_checkboxes() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getDataRange();
var data = range.getValues();
for (var row in data)
for (var col in data[row]) {
if (data[row][col] == 'X') sheet.getRange(+row+1,+col+1).insertCheckboxes()
}
}
To insert checkboxes without using formulas and 'X's you can run this function:
function insert_checkboxes() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var b1 = data[0][1]; // name from cell 'B1'
var c1 = data[0][2]; // name from cell 'C1'
for (var row=1; row<data.length; row++) {
if (data[row][3].indexOf(b1) > -1) sheet.getRange(row+1,2).insertCheckboxes();
if (data[row][3].indexOf(c1) > -1) sheet.getRange(row+1,3).insertCheckboxes();
}
}
Just in case. Instead of data[row][3].indexOf(b1) > -1 you can use a modern variant of the same condition data[row][3].includes(b1))
Related
So I have multiple files that have a column where I would like to update in the formula. However, there might be a certain cell that already has a value in it, but I don't want to replace it with the formula (see screenshot for reference).
I read some references here, but haven't found a similar case like mine.
This is the attempt that I do, but it's not working:
function updateWithFormula(){
/*** Input Data From Multiple Sources ****/
var sourceWorkbook = SpreadsheetApp.openById('')//id of the workbook
//Open tab 'Sheet1' and pull the data inside the script
var sourceSheet = sourceWorkbook.getSheetByName('Sheet1')
var source = sourceSheet.getDataRange().getDisplayValues()
for(row in source){
if (source[row][3]=="Update Value") {
//open files through link
var files = SpreadsheetApp.openByUrl(source[row][2]) //there's a link inside this column that linked to the file that I want to update
/*******insert formula *******/
//get range that want to be inserted by the formula, which is column S
//if the column S already have value in it, I don't want to do anything in it, however if it doesn't have value, I would like to put a formula
var result = files.getSheetByName('Sheet1').getRange("S2:S") //this is the column that I want to update
//set formula
for(r in result)
{
if(result[r] == "")
result[r].setFormula("=R"+ r+1)
}
}
}
}
Do you guys have any idea why my code is not working? Any advice for this case?
Thank you!
Objective
If I understood correctly, your objectives are the following:
Retrieve data from a "master" spreadsheet with information on which spreadsheets to update.
Loop through said data and locate the spreadsheets (represented as rows) that require updating.
Open those spreadsheets individually.
Update those spreadsheets rows with a sheets formula if a certain condition is met (in this case, that the cell is blank).
Issues
The for(var a in b) syntax in javaScript is used to iterate through object, not arrays. You should change it to:
for (var i = 0; i<source.length; i++){
//YOUR CODE
}
where: source[i] lets you access that specific row.
When you try to get the individual sheets' values, you are actually only getting the range, not the values themselves. You should replace this:
var result = files.getSheetByName('Sheet1').getRange("S2:S")
with this:
var sheet = files.getSheetByName('Sheet1');
var range = sheet.getRange("S2:S");
var values = range.getValues();
(You can read more about ranges and how they work here).
To input values into a spreadsheet, you should do it by using the setValue() method in the range class. Again, go here for more info. So, instead of:
result[r].setFormula("=R"+ r+1)
use:
var rangeToModify = sheet.getRange(j, 19); //LETTER S IS THE 19TH
rangeToModify.setValue("=R"+ (j+1)); //SET THE FORMULA
Final Code
function updateWithFormula(){
var sourceWorkbook = SpreadsheetApp.openById('')//id of the workbook
//Open tab 'Sheet1' and pull the data inside the script
var sourceSheet = sourceWorkbook.getSheetByName('Sheet1')
var source = sourceSheet.getDataRange().getDisplayValues()
for(var i = 0; i<source.length; i++){
if (source[i][3]=="Update Value"){
var files = SpreadsheetApp.openByUrl(source[row][2]);
var sheet = files.getSheetByName('Sheet1');
var range = sheet.getRange("S2:S");
var values = range.getValues();
//set formula
for(var j = 0; j<values.length; j++){
if (values[j] == ""){
//GET THE RANGE THAT YOU WANT TO MODIFY
var rangeToModify = sheet.getRange(j, 19); //LETTER S IS THE 19TH
rangeToModify.setValue("=R"+ (j+1)); //SET THE FORMULA
}
}
}
}
}
I believe your current situation and your goal are as follows.
"Sheet1" of sourceWorkbook has the Spreadsheet URLs and the value of "Update Value" in the columns "C" and "D", respectively.
You want to retrieve the Spreadsheet from the URL, and want to check the column "S2:S" of of "Sheet1" in the retrieved Spreadsheet, and want to put a formula like "=R"+ r+1 to the non-empty cells of the column "S".
In this case, how about the following modification?
Modification points:
var result = files.getSheetByName('Sheet1').getRange("S2:S") returns Class Range object. This cannot be used with for(r in result). This is the reason of but it's not working. This has already been mentioned by the Oriol Castander's answer.
When setFormula is used in a loop, the process cost becomes high.
When these points are reflected in your script, it becomes as follows.
Modified script:
function updateWithFormula() {
var sourceWorkbook = SpreadsheetApp.openById(''); // Please set your Spreadsheet ID.
var sourceSheet = sourceWorkbook.getSheetByName('Sheet1');
var source = sourceSheet.getDataRange().getDisplayValues();
source.forEach(r => {
if (r[3] == "Update Value") {
var sheet = SpreadsheetApp.openByUrl(r[2]).getSheetByName("Sheet1");
var rangeList = sheet.getRange("S2:S" + sheet.getLastRow()).getDisplayValues().flatMap(([e], i) => e == "" ? [`S${i + 2}`] : []);
if (rangeList.length > 0) {
sheet.getRangeList(rangeList).setFormulaR1C1("=R[0]C[-1]");
}
}
});
}
In this modification, the formula is put as the R1C1 using the range list. By this, I thought that the process cost will be able to be reduced a little.
References:
getRangeList(a1Notations)
setFormulaR1C1(formula)
I am trying to accomplish 2 things with Google Sheets. I have a Main sheet with data and I would like to:
Create new sheet with the name based on a cell value in Column B (accomplished)
Copy the rows containing those values in B in that new sheet
Part 1 works fine with this script:
function onOpen() {
var menu = [{
name : "Add",
functionName : "newSheet"
}
];
SpreadsheetApp.getActiveSpreadsheet().addMenu("Sheet", menu);
}
function newSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var templateSheet = ss.getActiveSheet();
var sheet1 = ss.getSheetByName("Sheet1")
var getNames = sheet1.getRange("B2:B").getValues().filter(String).toString().split(",");
for (var i = 0; i < getNames.length; i++) {
var copy = ss.getSheetByName(getNames[i]);
if (copy) {
Logger.log("Sheet already exists");
} else {
templateSheet.copyTo(ss).setName(getNames[i]);
ss.setActiveSheet(ss.getSheetByName(getNames[i]));
ss.moveActiveSheet(ss.getNumSheets());
}
}
}
The problem is when new sheets are created it copies the content of the main sheet. I would like to have only the rows containing the value in Column B copied into the new sheet.
Instead of using copyTo you might use one of the insertSheet methods of Class Spreadsheet, then copy rows having the required value in column B into the new sheet.
The specific code to copy the rows depends on what really need to copy (values, displayed values, formulas, cells formatting, rich text cell content formatting, notes, data validation, conditional formatting)
Let say that you are only interested in passing the values, you could use something like the following in the getNames for loop:
var data = sheet1.getDataRange().getValues().filter(row => row[1] === getNames[i])
var newSheet = ss.insertSheet(getNames[i]);
if( data.length > 0 ) newSheet.getRange(1,1,data.length,data[0].length).setValues(data);
Related
insertSheet advanced options
check for existence of a sheet in google spreadsheets
You can just add one more function to remove redundant rows from any sheet. Something like this:
function main() {
var sheet = SpreadsheetApp.getActiveSheet()
remove_rows("aaa", sheet); // remove all rows, that contain 'aaa' in first column
}
function remove_rows(value, sheet) {
var data = sheet.getDataRange().getValues();
var new_data = data.filter(x => x[0].indexOf(value)>=0)
sheet.getDataRange().clearContent();
sheet.getRange(1,1,new_data.length,new_data[0].length).setValues(new_data);
}
What I am trying to make is a workbook that users can select from a dropdown of a group of items (Sheet1, Column A) and then Row B lookup that selected item in sheet "Dataset" and return that value with integers that go from 0 to the corresponding stock quantity total in (Sheet "Dataset" column C)
here is a Sample spreadsheet
I got some awesome code from #iamblichus that will fill the dropdowns from the corresponding stock quantity
see his code here that I have somewhat implemented Here using a query formula to lookup the group stock quantities. I'm not sure how to make this happen across two sheets though.
Answer:
Extending the code that #iamblichus provided here, you can specify the Sheets from which to get the data from and use an onEdit() trigger to automatically change the dropdown when the cell is edited.
Code:
Attach this to the Sample Spreadsheet you provided:
function onEdit(e) {
var ss = SpreadsheetApp.getActive(); // Get the spreadsheet bound to this script
var dataSetSheet = ss.getSheetByName("Dataset"); // Get the sheet called "Working with script" (change if necessary)
var fillSheet = ss.getSheetByName("Sheet 1");
// Get the different values in column C (stock quantities):
var firstRow = 3;
var firstCol = 3;
var numRows = dataSetSheet.getLastRow() - firstRow + 1;
var stockQuantities = dataSetSheet.getRange(firstRow, firstCol, numRows).getValues();
var stockNames = dataSetSheet.getRange(firstRow, firstCol - 1, numRows).getValues();
// Iterate through all values in column:
for (var i = 0; i < stockQuantities.length; i++) {
Logger.log(stockNames);
Logger.log(stockQuantities);
var stockQuantity = stockQuantities[i][0];
var values = [];
// Create the different options for the dropdown based on the value in column C:
if (stockNames[i] == e.value) {
for (var j = 0; j <= stockQuantity; j++) {
values.push(j);
}
// Create the data validation:
var rule = SpreadsheetApp.newDataValidation().requireValueInList(values).build();
// Add the data validation to the corresponding cell in column B:
fillSheet.getRange(e.range.getRow(), 2).clear();
var dropdownCell = fillSheet.getRange(e.range.getRow(), 2).setDataValidation(rule);
}
}
}
Something to note:
I have put this as an onEdit() function because SpreadsheetApp is called in Read Only mode when inside a custom function and so no set*() methods can be called. This includes setDataValidation().
As per the Documentation, the Spreadsheet service is supported, however under 'Notes' it reads:
Read only (can use most get*() methods, but not set*()).
Cannot open other spreadsheets (SpreadsheetApp.openById() or SpreadsheetApp.openByUrl()).
References:
Stack Overflow - Dropdown auto generating a range based on a total
Google Apps Script - Range.clear() method
Google Apps Script - Simple Triggers
Google Apps Script - Custom Functions in Google Sheets
I am trying to write a Google Sheets Apps Script function that checks the content of the current active cell, matches it to the content of another cell, then moves the cursor according to the result of that check.
For a spreadsheet as this example one:
https://docs.google.com/spreadsheets/d/1kpuVT1ZkK0iOSy_nGNPxvXPTFJrX-0JgNmEev6U--5c/edit#gid=0
I would like the user to go to D2, enter a value followed by Tab, then while the active cell is in E2, the function will check if the value in D2 is the same in B2. If it is, stays in E2.
Then we enter the value in E2 followed by Tab, the function checks if it's the same as C2, if it is, then moves from F2 down and left twice to D3. So if all the values are entered correctly, the cursor zig-zags between the cells in D, E and F as shown below:
The closest I could find is the answer to the one below, but it involves clicking on a method in the menu each time:
Move sheet rows on based on their value in a given column
I imagine the function could be triggered at the beginning of editing the document, then it keeps moving the cursor until the document is completed, at which point the function can be stopped.
Any ideas?
EDIT: what I've tried so far:
I have managed to change the position to a hard-coded position 'D3' and to create a function that moves one down with these functions:
function onOpen() {
var m = SpreadsheetApp.getUi().createMenu('Move');
m.addItem('Move to D3', 'move').addToUi();
m.addItem('Move to one below', 'move2').addToUi();
m.addItem('Move down left', 'move_down_left').addToUi();
}
function move() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var range = s.getRange('D3');
s.setActiveRange(range);
}
function move2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var r = s.getActiveRange();
var c = r.getCell(1,1);
var target = s.getRange(c.getRow() + 1, c.getColumn());
s.setActiveRange(target);
}
function move_down_left() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var r = s.getActiveRange();
var c0 = r.getCell(1,1);
var r1 = s.getRange(c0.getRow(), c0.getColumn() - 1);
var c1 = r1.getCell(1,1);
var r2 = s.getRange(c1.getRow(), c1.getColumn() - 2);
var c2 = r2.getCell(1,1);
if (c1.getValue() == c2.getValue()) {
var target = s.getRange(c1.getRow() + 1, c1.getColumn() - 1);
s.setActiveRange(target);
}
}
As I mentioned in my comment, you want to use a simple trigger function (so that it works for all users without requiring them to first authorize the script). There are naturally some limitations of simple triggers, but for the workflow you describe, they do not apply.
A key principle of a function receiving the on edit trigger invocation is that it has an event object with data about the cell(s) that were edited:
authMode:
A value from the ScriptApp.AuthMode enum.
oldValue:
Cell value prior to the edit, if any. Only available if the edited range is a single cell. Will be undefined if the cell had no previous content.
range:
A Range object, representing the cell or range of cells that were edited.
source:
A Spreadsheet object, representing the Google Sheets file to which the script is bound.
triggerUid:
ID of trigger that produced this event (installable triggers only).
user:
A User object, representing the active user, if available (depending on a complex set of security restrictions).
value:
New cell value after the edit. Only available if the edited range is a single cell.
Of these, we will use range and value. I will leave the business case of handling edits to multiple-cell ranges to you. Stack Overflow is, after all, not where you obtain turnkey solutions ;)
function onEdit(e) {
if (!e) throw new Error("You ran this from the script editor");
const edited = e.range;
if (edited.getNumRows() > 1 || edited.getNumColumns() > 1)
return; // multicell edit logic not included.
const sheet = edited.getSheet();
if (sheet.getName() !== "your workflow sheet name")
return;
// If the user edited a specific column, check if the value matches that
// in a different, specific column.
const col = edited.getColumn(),
advanceRightColumn = 5,
rightwardsCheckColumn = 2;
if (col === advanceRightColumn) {
var checkedValue = edited.offset(0, rightwardsCheckColumn - col, 1, 1).getValue();
if (checkedValue == e.value) // Strict equality may fail for numbers due to float vs int
edited.offset(0, 1, 1, 1).activate();
else
edited.activate();
return;
}
const endOfEntryColumn = 8,
endCheckColumn = 3,
startOfEntryColumn = 4;
if (col === endOfEntryColumn) {
var checkedValue = edited.offset(0, endCheckColumn - col, 1, 1).getValue();
if (checkedValue == e.value)
edited.offset(1, startOfEntryColumn - col, 1, 1).activate();
else
edited.activate();
return;
}
}
As you digest the above, you'll note that you are required to supply certain values that are particular to your own workflow, such as a sheet name, and the proper columns. The above can be modified in a fairly straightforward manner to advance rightward if the edited column is one of several columns, using either a constant offset to the respective "check" column, or an array of respectively-ordered offsets / target columns. (Such a modification would almost certainly require the use of Array#indexOf.)
A caveat I note is that strict equality === fails if your edits are numbers representable as integers, because Google Sheets will store the number as a float. Strict equality precludes type conversion by definition, and no int can ever be the exact same as a float. Thus, the generic equality == is used. The above code will not equate a blank check cell and the result of deleting content.
Method references:
Range#offset
Range#activate
I'd like to be able to delete an entire row in a Google Spreadsheets if the value entered for say column "C" in that row is 0 or blank. Is there a simple script I could write to accomplish this?
Thanks!
I can suggest a simple solution without using a script !!
Lets say you want to delete rows with empty text in column C.
Sort the data (Data Menu -> Sort sheet by column C, A->Z) in the sheet w.r.t column C, so all your empty text rows will be available together.
Just select those rows all together and right-click -> delete rows.
Then you can re-sort your data according to the column you need.
Done.
function onEdit(e) {
//Logger.log(JSON.stringify(e));
//{"source":{},"range":{"rowStart":1,"rowEnd":1,"columnEnd":1,"columnStart":1},"value":"1","user":{"email":"","nickname":""},"authMode":{}}
try {
var ss = e.source; // Just pull the spreadsheet object from the one already being passed to onEdit
var s = ss.getActiveSheet();
// Conditions are by sheet and a single cell in a certain column
if (s.getName() == 'Sheet1' && // change to your own
e.range.columnStart == 3 && e.range.columnEnd == 3 && // only look at edits happening in col C which is 3
e.range.rowStart == e.range.rowEnd ) { // only look at single row edits which will equal a single cell
checkCellValue(e);
}
} catch (error) { Logger.log(error); }
};
function checkCellValue(e) {
if ( !e.value || e.value == 0) { // Delete if value is zero or empty
e.source.getActiveSheet().deleteRow(e.range.rowStart);
}
}
This only looks at the value from a single cell edit now and not the values in the whole sheet.
I wrote this script to do the same thing for one of my Google spreadsheets. I wanted to be able to run the script after all the data was in the spreadsheet so I have the script adding a menu option to run the script.
/**
* Deletes rows in the active spreadsheet that contain 0 or
* a blank valuein column "C".
* For more information on using the Spreadsheet API, see
* https://developers.google.com/apps-script/service_spreadsheet
*/
function readRows() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var rowsDeleted = 0;
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
if (row[2] == 0 || row[2] == '') {
sheet.deleteRow((parseInt(i)+1) - rowsDeleted);
rowsDeleted++;
}
}
};
/**
* Adds a custom menu to the active spreadsheet, containing a single menu item
* for invoking the readRows() function specified above.
* The onOpen() function, when defined, is automatically invoked whenever the
* spreadsheet is opened.
* For more information on using the Spreadsheet API, see
* https://developers.google.com/apps-script/service_spreadsheet
*/
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Remove rows where column C is 0 or blank",
functionName : "readRows"
}];
sheet.addMenu("Script Center Menu", entries);
};
Test spreadsheet before:
Running script from menu:
After running script:
I was having a few problems with scripts so my workaround was to use the "Filter" tool.
Select all spreadsheet data
Click filter tool icon (looks like wine glass)
Click the newly available filter icon in the first cell of the column you wish to search.
Select "Filter By Condition" > Set the conditions (I was using "Text Contains" > "word")
This will leave the rows that contain the word your searching for and they can be deleted by bulk selecting them while holding the shift key > right click > delete rows.
This is what I managed to make work. You can see that I looped backwards through the sheet so that as a row was deleted the next row wouldn't be skipped. I hope this helps somebody.
function UpdateLog() {
var returnSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('RetLog');
var rowCount = returnSheet.getLastRow();
for (i = rowCount; i > 0; i--) {
var rrCell = 'G' + i;
var cell = returnSheet.getRange(rrCell).getValue();
if (cell > 0 ){
logSheet.
returnSheet.deleteRow(i);
}
}
}
quite simple request. Try this :
function try_It(){
deleteRow(2); //// choose col = 2 for column C
}
function deleteRow(col){ // col is the index of the column to check for 0 or empty
var sh = SpreadsheetApp.getActiveSheet();
var data = sh.getDataRange().getValues();
var targetData = new Array();
for(n=0;n<data.length;++n){
if(data[n][col]!='' && data[n][col]!=0){ targetData.push(data[n])};
}
Logger.log(targetData);
sh.getDataRange().clear();
sh.getRange(1,1,targetData.length,targetData[0].length).setValues(targetData);
}
EDIT : re-reading the question I'm not sure if the question is asking for a 'live' on Edit function or a function (like this above) to apply after data has been entered... It's not very clear to me... so feel free to be more accurate if necessary ;)
There is a simpler way:
Use filtering to only show the rows which you want to delete. For example, my column based on which I want to delete rows had categories on them, A, B, C. Through the filtering interface I selected only A and B, which I wanted to delete.
Select all rows and delete them. Doing this, in my example, effectively selected all A and B rows and deleted them; now my spreadsheet does not show any rows.
Turn off the filter. This unhides my C rows. Done!
There is a short way to solve that instead of a script.
Select entire data > Go to menu > click Data tab > select create filter > click on filter next to column header > pop-up will appear then check values you want to delete > click okay and copy the filtered data to a different sheet > FINISH
reading your question carefully, I came up with this solution:
function onOpen() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// create menu
var menu = [{name: "Evaluate Column C", functionName: "deleteRow"}];
// add to menu
ss.addMenu("Check", menu);
}
function deleteRow() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get active/selected row
var activeRow = ss.getActiveRange().getRowIndex();
// get content column C
var columnC = ss.getRange("C"+activeRow).getValue();
// evaluate whether content is blank or 0 (null)
if (columnC == '' || columnC == 0) {
ss.deleteRow(parseInt(activeRow));
}
}
This script will create a menu upon file load and will enable you to delete a row, based on those criteria set in column C, or not.
This simple code did the job for me!
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet(); // get active spreadsheet
var activeRow = ss.getActiveRange().getRowIndex(); // get active/selected row
var start=1;
var end=650;
var match='';
var match2=0; //Edit this according to your choice.
for (var i = start; i <= end; i++) {
var columnC = ss.getRange("C"+i).getValue();
if (columnC ==match || columnC ==match2){ ss.deleteRow(i); }
}
}
The below code was able to delete rows containing a date more than 50 days before today in a particular column G , move these row values to back up sheet and delete the rows from source sheet.
The code is better as it deletes the rows at one go rather than deleting one by one. Runs much faster.
It does not copy back values like some solutions suggested (by pushing into an array and copying back to sheet). If I follow that logic, I am losing formulas contained in these cells.
I run the function everyday in the night (scheduled) when no one is using the sheet.
function delete_old(){
//delete > 50 day old records and copy to backup
//run daily from owner login
var ss = SpreadsheetApp.getActiveSpreadsheet();
var bill = ss.getSheetByName("Allotted");
var backss = SpreadsheetApp.openById("..."); //backup spreadsheet
var bill2 = backss.getSheetByName("Allotted");
var today=new Date();
//process allotted sheet (bills)
bill.getRange(1, 1, bill.getMaxRows(), bill.getMaxColumns()).activate();
ss.getActiveRange().offset(1, 0, ss.getActiveRange().getNumRows() - 1).sort({column: 7, ascending: true});
var data = bill.getDataRange().getValues();
var delData = new Array();
for(n=data.length-1; n>1; n--){
if(data[n][6] !=="" && data[n][6] < today.getTime()-(50*24*3600*1000) ){ //change the condition as per your situation
delData.push(data[n]);
}//if
}//for
//get first and last row no to be deleted
for(n=1;n<data.length; n++){
if(data[n][6] !=="" && data[n][6] < today.getTime()-(50*24*3600*1000) ){
var strow=n+1 ; //first row
break
}//if
}//for
for(n=data.length-1; n>1; n--){
if(data[n][6] !=="" && data[n][6] < today.getTime()-(50*24*3600*1000) ){
var ltrow=n+1 ; //last row
break
}//if
}//for
var bill2lr=bill2.getLastRow();
bill2.getRange((bill2lr+1),1,delData.length,delData[0].length).setValues(delData);
bill.deleteRows(strow, 1+ltrow-strow);
bill.getRange(1, 1, bill.getMaxRows(), bill.getMaxColumns()).activate();
ss.getActiveRange().offset(1, 0, ss.getActiveRange().getNumRows() - 1).sort({column: 6, ascending: true}); //get back ordinal sorting order as per column F
}//function