I donĀ“t know how to code or write scripts.
I have a recording script/macro that order/sort a range according to 2 conditions and is working great for my needs.
I would like however to add a functionality but don't know how to do it, I would like the script would add a blank row between the 1 of the conditions (column 34).
The first condition of the below script is to order/sort according to 2 words in column 34 (or AH), is it possible between that "frontier" to automatically add a blank row?
This is the code I have:
/** #OnlyCurrentDoc */
function Navios() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('u3:Ak').activate()
.sort([{column: 34, ascending: true}, {column: 22, ascending: true}]);
};
This is a example sheet (pls ignore the conditional format). I want a blank row between words "A decorrer" and "Concluido" in column AH (or 34)
Can someone help?
Thanks.
I believe your goal as follows.
You want to insert new row between the value of A decorrer and the value of Concluido at the column "AH" after the sort script was run in your function Navios().
Modification points:
In this case, I would like to suggest the following flow.
Run your script for sorting in the function Navios().
Add a script for inserting new row in the function Navios().
Retrieve values of the column "AH".
Retrieve the boundary of the values A decorrer and Concluido.
Insert new row between the values.
When above points are reflected to your script, it becomes as follows.
Modified script:
function Navios() {
var spreadsheet = SpreadsheetApp.getActiveSheet();
var range = spreadsheet.getRange('u3:Ak'); // Modified
range.activate().sort([{column: 34, ascending: true}, {column: 22, ascending: true}]); // Modified
// I added below script.
var values = range.offset(0, 13, range.getNumRows(), 1).getValues();
for (var i = 0; i < values.length; i++) {
if (values[i][0] != values[0][0]) {
spreadsheet.insertRowAfter(i + 2);
// spreadsheet.deleteRow(spreadsheet.getMaxRows());
break;
}
}
}
In this modified script, when the script is run, a row is added to the sheet. By this, the number of rows are increased every run. When you want to keep the number of rows when the script is run, please use the line of // spreadsheet.deleteRow(spreadsheet.getMaxRows());. By this, when new row is inserted, the bottom row of the sheet is deleted. By this, the number of rows are kept. But I'm not sure about your actual situation. So I suggested it as the additional information.
Reference:
insertRowAfter(afterPosition)
Related
I have the following code as a starting point. I want to select the entire column where row 3 contains a specific date value (it will be the date of the previous Monday; I have a formula returning this date in cell E1).
function selectDate() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
ss.getRange(3,?,1,ss.getMaxColumns()).activate();
}
Basically, the getRange column value would be interpreted as something like: "Find the column number where the value in row 3 is equal to the value in cell E1".
Any ideas would be very helpful, even if it's using a totally different method to achieve the same thing. Thank you so much!
In your situation, how about the following sample script?
Sample script:
function selectDate() {
var sheet = SpreadsheetApp.getActiveSheet();
var searchValue = sheet.getRange("E1").getDisplayValue();
var res = sheet.getRange(3, 1, 1, sheet.getLastColumn()).createTextFinder(searchValue).matchEntireCell(true).findNext();
if (!res) return;
var column = res.getColumn();
sheet.getRange(1, column, sheet.getLastRow()).activate(); // Here, the found column is activated.
Browser.msgBox("Found column number is " + column); // Here, the found column number is shown in a dialog.
}
From your situation, I thought that getRange(3,?,1,ss.getMaxColumns()) in your script might be getRange(3, 1, 1, sheet.getLastColumn()).
When this script is run, row 3 is searched using the value of cell "E1". When the value is found, as a sample, the found column is activated and the column number is shown in a dialog. This is a sample. Please modify this for your actual situation.
Note:
If no column is selected, it is considered that the value of cell "E1" is not found in row 3. At that time, can you provide the detail of your Spreadsheet? By this, I would like to modify it.
Reference:
createTextFinder(findText) of Class Range
[How My Current Google Sheet works]
I have a table that has the following headers in the exact order and it has all sorts of data.
Date, Ticket ID, Ticket Link, Ticket Type, Category, Subcategory, Status, Customer
What the sheet looks like (Certain information has been removed)
[Goal]
I want to be able to bring all the rows to the top that has a status with anything except for "Finished." And at the same time sort the entire table with the "Date" column. So I want this to be continuous and want the rows with the "Finished" "Status" to all be under the rows with the rest of the statuses.
[What's the issue]
I've constructed the following script, to sort the data, however there are two main issues.
I don't know how to have the system sort the rows whenever the user makes changes to the cell of the "Status" column.
I was able to sort the data with the following script by "Date" and "Status," however the rows with the "Finished" status are in between the other statuses. What I want to do is, if there are rows with the "Status" as "Finished" then I want them to be below any other statuses. And within those rows with the "Finished" status, they should be sorted by the "Date" column.
// Global veriables
var app = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Status");
var dateColumn = 1;
var statusColumn = 7;
var lastRow = app.getLastRow();
var lastColumn = app.getLastColumn();
function dynamicDataSorting() {
var sortingRange = app.getRange(16, 1, lastRow - 1, lastColumn)
sortingRange.sort([{column: dateColumn, ascending: false}, {column: statusColumn, ascending: false}]);
}
You were very close to the final solution. This is how I would approach it:
Create an auxiliary column (statusAux) with this formula: =if(C2="finished",0,1) where column C is your statusColumn. You can hide it with hideColumn(colmun), hideColumns(columnIndex) or from the web.
Use the following function to sort your data:
function onEdit(e) {
range.sort([{column: statusAux, ascending: false}, {column: dateColumn, ascending: true}]);
}
You can define your range inside or outside of the function, it has the same effect.
To understand how triggers work check out Simple Triggers and onEdit(e)
If you want to try Arrayformula:
You can create a separate sheet and apply below formula:
=Arrayformula(SUBSTITUTE(query(Arrayformula(SUBSTITUTE(A2:E8,"Finished","ZzFinished")),"Select * Order by Col5,Col1 asc"),"ZzFinished","Finished"))
Change the data Range and 'Col' number per your need!
I've heavily edited the original question I posted, as i have solved some of the issue myself. I'm now stuck on just one thing.
function payINVOICE() {
var ss = SpreadsheetApp.getActive();
var ds = SpreadsheetApp.openById("14imcEob2qIZbH6AjGYtf16MJxbnfkhQn1ae4jR-Nzq4");
var srcSheet = ss.getSheetByName("INVOICE_ENTRY");
var dstSheet = ds.getSheetByName("INVOICE_ENTRY");
var data_range = srcSheet.getRange('B4:J100');
var data_data = data_range.getValues();
var data_clean = data_data.filter(function (r) {return r[1]});
var clear_range = srcSheet.getRange('B4:I100');
var lr = dstSheet.getLastRow();
dstSheet.getRange(lr+1, 2,data_clean.length,9).setValues(data_clean);
clear_range.clear();
}
This code checks the range B4:J100 for a value in Column B.
If there is a value and the script is run, it copies those rows onto dstSheet.
My role is marking invoices as paid or not.
The dstSheet will already contain the data, which is pulled back into the srcSheet with a query. Column K is not part of the original query.
If I mark a row as "PAID" in column K on the srcSheet, I want the code to take the data_data variable and overwrite what is already in the dstSheet, so that the query then pulls the data back into srcSheet with column J then showing "PAID".
It means I can then change column K to "NOT PAID", run the script again and it will over-write the "PAID".
This makes better sense than my last post and I am so close to achieving what I need, just stuck on this last bit.
If you simply want to monitor the changes between the two mentioned sheets, it would be much easier to use an onEdit(e) trigger which will tell you which cell has been edited.
Snippet
function payINVOICE(e) {
var srcSheet = SpreadsheetApp.getActiveSheet(); //gets the active sheet which is supposed to be source sheet
var dstSheet = SpreadsheetApp.openById('DEST_SHEET_ID').getSheetByName('INVOICE_ENTRY'); //gets the destination sheet
if (e.range.getSheet().getName() == 'INVOICE_ENTRY' && e.range.getColumn() == 11) { //e specifies where the edit has been made - therefore this if condition checks if the edit is in the INVOICE ENTRY sheet and if the column is the K column
var row =e.range.getRow(); //this gathers the row at which the edit has been made
var data = srcSheet.getRange(row, 2, 1, 10).getValues(); //this gathers the data corresponding to the row at which the edit has been made
dstSheet.getRange(row, 2, 1, 10).setValues(data); //this sets the data into the corresponding row in the destination sheet
}
Explanation
The above code uses the onEdit(e) installable trigger and the e event object. In this way, when an edit is being made on the srcSheet on the 11th column (aka K column) and the sheet name is "INVOICE_ENTRY", then the row at which the change has been made is kept in the row variable. Afterwards, the corresponding row of data is kept in the data variable; the getRange(row, 2, 1, 10) references the range for the row at which the change on the K column has been made. In order to update the dstSheet, the data value is set to the according range using setValues(data).
Installing the trigger
To make the payINVOICE(e) function trigger on an edit action, you need to install an onEdit trigger.
This is being done by accessing the project's triggers by clicking this icon:
After that, you just need to create a new trigger by clicking the Add trigger button and create a trigger with the following settings:
Trying the function
In order to try the behavior for this, you just need to make an edit on the srcSheet on the K column and this change will be reflected in the destSheet.
Note
The ranges that have been used in this script are chosen considering the fact that:
K column consists of the PAID/NOT PAID text;
The srcSheet and the dstSheet have the data wanted in the same ranges.
You might need to customize these according to your sheet and add the needed formulas/filters you have mentioned.
Reference
Apps Script Installable Triggers;
Apps Script Event Objects.
I'm trying to modify the code from the below forum post to help fit my needs:
https://productforums.google.com/forum/#!topic/docs/ehoCZjFPBao/discussion
function onEdit() {
// moves a row from a sheet to another when a magic value is entered in a column
// adjust the following variables to fit your needs
// see https://productforums.google.com/d/topic/docs/ehoCZjFPBao/discussion
var sheetNameToWatch = "Transfers";
var columnNumberToWatch = 15; // column A = 1, B = 2, etc.
var valueToWatch = "yes";
var sheetNameToMoveTheRowTo = "Archive";
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveCell();
if (sheet.getName() == sheetNameToWatch && range.getColumn() == columnNumberToWatch && range.getValue() == valueToWatch) {
var targetSheet = ss.getSheetByName(sheetNameToMoveTheRowTo);
var targetRange = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
sheet.getRange(range.getRow(), 1, 1, sheet.getLastColumn()).moveTo(targetRange);
sheet.deleteRow(range.getRow());
}
}
What this code does is moves a row from one tab to another when a specific value is entered (in the code above, it moves a row from "Transfers" to "Archive" when the word "yes" is entered in column 15 (Column O).
My needs are a little bit different. I cannot have a user moving one row at a time because there may be multiple rows involved and combined together, they will need to meet certain criteria (e.g. do the amounts in all rows balance to 0, is the same account used, etc.). This I believe is a rudimentary data validation.
Therefore, I tried to make a formula so that if I entered the word yes in cell F8, it populates the word "Send" (used to be "yes", changed for clarity) to column O. If the word yes is entered in cell F8, then every row with the word "Send" should be moved to the Archive Column. The problem is, the above code relied on a function called OnEdit, and populating the rows with the word Send via formula does not trigger the script to run. The above code needs the user to manually enter the correct keyword in order to move the rows over.
Can someone help to modify or rewrite the code so that it looks for a user to manually type in the keyword "yes" in cell F8, and then have it move any row that had the word "Send" populated by a formula in Column O?
The below Google Sheet is a simplified example of what I'm trying to do. Additional comments can be found on the "MASTER - DO NOT EDIT" tab.
https://docs.google.com/spreadsheets/d/1iajS90qvwEOGVnl2lpDbVtcI532OO8n4NLZEBDUpVzA/edit#gid=398066315
Thanks for looking. If anyone needs additional info, please let me know.
~~~~~~~~~~~~~~~~~~~~~~~~~~~
EDIT: I tried out Cooper's code below, and it seems to work. I'm a novice at this, but I've managed to add a few things to make it run onEdit, and to delete the initial trigger word.
An example of this slightly tweaked code running can be found below:
function onEdit() {
archiveRows();
clearCells();
}
function archiveRows()
{
/*installable trigger rows 10-13 https://developers.google.com/apps-script/guides/triggers/installable
if(!projectTriggerExists()
{
ScriptApp.newTrigger('myFunction')
.forSpreadsheet(ss)
.onOpen()
.create();
}//by wrapping the trigger creation like this you don't have to worry about creating unwanted triggers.
*/
var ss=SpreadsheetApp.getActive();
var sh0=ss.getSheetByName('Transfers'); //sh0 = Transfers tab
var rg0=sh0.getDataRange(); //rg0 = Range of sh0, This is functionally equivalent to creating a Range bounded by A1 and (Range.getLastColumn(), Range.getLastRow()). https://developers.google.com/apps-script/reference/spreadsheet/sheet#getdatarange
var sh1=ss.getSheetByName('Archive'); //sh1 = Archive tab
var vals=rg0.getValues(); //Returns the rectangular grid of values for this range. Returns a two-dimensional array of values, indexed by row, then by column. The values may be of type Number, Boolean, Date, or String, depending on the value of the cell. Empty cells will be represented by an empty string in the array. Remember that while a range index starts at 1, 1, the JavaScript array will be indexed from [0][0]. https://developers.google.com/apps-script/reference/spreadsheet/range#getvalues
for(var i=vals.length-1;i>11;i--) //When deleting rows it is better to start from the bottom otherwise deleted rows will mess up your loop indexing
{
if(vals[i][14]=='SEND') //If column 14 in the range has the word 'SEND' then run the next lines of code. 14 is column O. Column 0 is 15, but see note above regarding JavaScript array being indexed from [0][0]
{
sh1.appendRow(vals[i]);
sh0.deleteRow(i+1);//indexes start at zero but rows start at one
}
}
}
//This could be accomplished with SpreadsheetApp.getActive().getActiveSheet().clear();
function clearCells()
{
//https://stackoverflow.com/questions/9268570/i-need-a-button-to-clear-cells-in-a-google-spreadsheet
var ss=SpreadsheetApp.getActive();
ss.getRange('A1:A1').clearContent()
}
function projectTriggerExists(functionName)
{
if(functionName)
{
var allTriggers=ScriptApp.getProjectTriggers();
var funcExists=false;
for(var i=0;i<allTriggers.length;i++)
{
var trigger=allTriggers[i];
if(allTriggers[i].getHandlerFunction()==functionName)
{
funcExists=true;
break;
}
}
}
return funcExists;
}
The nature of the spreadsheet is that I will need to duplicate the "Transfers" tab in the original post a dozen times so that it can be used by multiple people at once (data being entered in by the user essentially over 12 "forms"). All the data put into these dozen "Transfers" tabs should rout to the same "Archive" tab. However, I'd also like to build in a rudimentary routing system, so that instead of having everything rout to one tab, different code words send the lines to different "Archive" tabs. For instance, "OK+Send+Staff1" sends the line to a tab called Staff1Archive, "OK+Send+Staff2" sends to a tab called Staff2Archive, "OK+Send+Staff3" sends to a tab called Staff3Archive, etc.
Can anyone help with this code as well?
I think this will do it for you.
function archiveRows()
{
var ss=SpreadsheetApp.getActive();
var sh0=ss.getSheetByName('Transfers');
var rg0=sh0.getDataRange();
var sh1=ss.getSheetByName('Archive');
var vals=rg0.getValues();
for(var i=vals.length-1;i>11;i--)
{
if(vals[i][14]=='SEND')
{
sh1.appendRow(vals[i]);
sh0.deleteRow(i+1)
}
}
}
I have a requirement where I need to take values from one column (or more) and copy them to another sheet in the next available column (s). I have written a script like this. It does copy the values from one column but it has no way to move forward for taking another snapshot of new data in same source column to destination sheet's next free column.
*//keep a copy of Sales numbers for every month
function readSalesNum() {
var sheetFrom = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sales plan");
var sheetTo = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SalesRecordsMonthly");
// Copy from 17th row, 4th column, all rows for one column
var rangeToCopy = sheetFrom.getRange(17, 4, sheetFrom.getMaxRows(), 1);
//Paste to another sheet from first cell onwards
rangeToCopy.copyTo(sheetTo.getRange(1, 1));
}*
I am sorry about the poor formatting :( I need to modify this script to mention any set of columns from source sheet and copy them to destination sheet. And for new month, it should do the same in next set of columns, instead of overwriting as it does now. Also, the copy should happen only for values which is missing yet. I know there's an option of ContentOnly in script but not sure how to use it.
If I understood correctly, here is a code that does what you wanted, ie get the values from one sheet in column4 from row 17 and copy it to the other sheet without overwriting to columns starting at row 1
function readSalesNum() {
var sheetFrom = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sales plan");
var sheetTo = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SalesRecordsMonthly");
// Copy from 17th row, 4th column, all rows for one column
var valuesToCopy = sheetFrom.getRange(17, 4, sheetFrom.getLastRow(), 1).getValues();
//Paste to another sheet from first cell onwards
sheetTo.getRange(1,sheetTo.getLastColumn()+1,valuesToCopy.length,1).setValues(valuesToCopy);
}
test sheet here, make a copy to run the script - view only
This covers only the first part of your question, the second part was a bit confusing (as mentioned in my comment above).
The entire 4th column & 17th row onward can be appended as a column into a different sheet with this approach also.
var sheetfrom = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sheetfrom');
var sheetto = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sheetto');
sheetfrom.getRange(17, 4, sheetfrom.getLastRow(), 1).copyTo(sheetto.getRange(1,
sheetTo.getLastColumn()+1, sheetfrom.getLastRow()-17, 1), {
contentsOnly: true
});
This also overcomes the empty cell problem of copying data from some Formula outputs.
Try this
*//keep a copy of Sales numbers for every month
function readSalesNum() {
var sheetFrom = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sales plan");
var sheetTo = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SalesRecordsMonthly");
// Copy from 17th row, 4th column, all rows for one column
var rangeValues = sheetFrom.getRange(17, 4, 1, sheetFrom.getMaxRows()).getValues();
//Paste to another sheet from first cell onwards
sheetTo.appendRow(rangeValues[0]);
}
This will do what you want with the range you suggested. However, looks like you're trying to get two different ranges so you'll have to accommodate that.