I'm using an add-on (toTabs) that separates a sheet of data based on the value in column H. It generates a new sheet for each unique value in H2:H, and then places a filter() in cell A2 on each sheet. This works wonderfully, except my next step is to export each sheet to .csv, and I need to get rid of H:H before doing that.
I'm trying to write a script that changes each filter from A2:H to A2:G and either ignores the rest of the formula, or replaces the text that the filter searches for to the sheet name, then loops through every sheet except for specific sheets. I feel like I'm 95% of the way there, but the loop isn't working quite right. Here's what I have so far:
function Loop() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
// Array holding the names of the sheets to exclude from the execution
var exclude = ["Raw Input","Remove Chuff","Filtered Input","Final Input","Data Values","blank","totabs"];
for(var s in allsheets){
var sheet = allsheets[s];
// Stop iteration execution if the condition is meet.
if(exclude.indexOf(sheet.getName())==-1) continue;
var cell = s.getRange("A2");
cell.setFormula('=Filter(\'Data Values\'!A2:G,\'Data Values\'!H2:H="'+ SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName() + '")');
s.getRange('H:H').activate();
s.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
s.getRange('F:F').activate();
s.getActiveRangeList().setNumberFormat('00000');
}
}
Here's a copy of the file with all the irrelevant tabs hidden. Any help would be welcome.
Try this:
function Loop() {
var ss=SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
var exclude = ["Raw Input","Remove Chuff","Filtered Input","Final Input","Data Values","blank","totabs"];
for(var i=0;i<allsheets.length;i++){
var sheet = allsheets[i];
if(exclude.indexOf(sheet.getName())!=-1) continue;
sheet.getRange("A2").setFormula('=Filter(\'Data Values\'!A2:G,\'Data Values\'!H2:H="'+ sheet.getName() + '")');
sheet.getRange(1,8,sheet.getLastRow(),1).clear({contentsOnly: true, skipFilteredRows: true});
sheet.getRange(1,6,sheet.getLastRow(),1).setNumberFormat('00000');
}
}
Related
I am trying to copy data from 1 spreadsheet to another, I have successfully implemented something i found online that works with a specific range
function cloneGoogleSheet() {
// source doc
var sss = SpreadsheetApp.openById("spreadsheetkey1");
// source sheet
var ss = sss.getSheetByName('_tab_name_source');
// Get full range of data
var SRange = ss.getRange(7,3,5,1);
// get A1 notation identifying the range
var A1Range = SRange.getA1Notation();
// get the data values in range
var SData = SRange.getValues();
// target spreadsheet
var tss = SpreadsheetApp.openById("spreadsheetkey2");
// target sheet
var ts = tss.getSheetByName('tab_name_destination');
// Clear the Google Sheet before copy
//ts.clear({contentsOnly: true});
// set the target range to the values of the source data
ts.getRange(A1Range).setValues(SData);
};
The above piece coding work perfectly however I need to copy 18 different ranges that i cant just merge into 1 range. I considered the option of using the above however "multiplying" it 18 times for each range that however seems like a very inelegant solution.
I found a working solution that works if it stays within the same spreadsheet since it uses copyto instead of get/set values. The values part works perfectly since it doesnt mess with merge cells formatting. I have been struggling past 2-3 hours in merging the below-working code with elements from the first code to make a working script.
function test () {
try {
var spread = SpreadsheetApp.openById("spreadsheetkey");
var sheet = spread.getSheetByName("tab_name_source");
var rlist = sheet.getRangeList(["c7:c11", "g7:g11", "k7:k11"]);
sheet = spread.getSheetByName("tab_name_destination");
for( var i=0; i<rlist.getRanges().length; i++ ) {
var r1 = rlist.getRanges()[i];
var r2 = sheet.getRange(r1.getA1Notation());
r1.copyto(r2);
}
}
catch(err) {
Logger.log(err);
}
}
I tried initially to adapt the 2nd piece of coding to using setvalues however i had not been able to succesfully implement the part of getvalues within the scope of this code. I figured once I got this piece of code working with get and set values instead of Copyto i would only need to add the spreadsheetid of the other spreadsheet to get the final result
Try this:
function myFunction() {
var sourceSS = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = sourceSS.getSheetByName("sheetname");
var targetSS = SpreadsheetApp.openById("spreadsheet id here");
var targetSheet = targetSS.getSheetByName("Sheet1");
var ranges = ["C7:C11", "G7:G11", "K7:K11"];
ranges.forEach(range => {
var data = sourceSheet.getRange(range).getValues();
targetSheet.getRange(range).setValues(data);
})
}
Source sheet:
Destination sheet:
References:
setValues()
getValues()
I have a sheet where when I change a specific cell to "YES", I need a template sheet to be copied to a new version and named as per the value of a cell on the current row.
I'm having trouble working out how to get the value of the first cell in the row selected. This is what I have so far (I know this is wrong):
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var currentCell = sheet.getCurrentCell();
if (currentCell = "YES")
{
SpreadsheetApp.getActiveSpreadsheet().toast("New change control sheet added to workbook.","Change Control",15);
var sourceRow = ss.getActiveRange().getRowIndex();
var tabName = ss.getRange(cell,1).getValues();
ss.getSheetByName("CCTemplate").showSheet()
.activate();
ss.setActiveSheet(ss.getSheetByName('CCTemplate'), true);
ss.duplicateActiveSheet();
ss.setActiveSheet(ss.getSheetByName('CCTemplate'), true);
ss.getActiveSheet().hideSheet();
ss.setActiveSheet(ss.getSheetByName('Copy of CCTemplate'), true);
ss.getActiveSheet().setName("CC" & tabName);
}
}
Any ideas?
function onEdit(e) {
var sh=e.range.getSheet();
if(sh.getName()=='Your Sheet Name' && e.value=="YES") {
e.source.toast="New change control sheet added to workbook.","Change Control",15);
var tabName=sh.getRange(e.range.rowStart,1).getValue();
var tsh=e.source.getSheetByName('CCTemplate');
var csh=tsh.copyTo(e.source);
csh.setName('CC'+tabName);
}
}
You should avoid using activate in your scripts especially in simple triggers where you have to finish in 30 seconds. I think this code does the same thing that you intended for your code. One significant difference is that I use the information that comes in the event object that comes with the trigger. You should add the code Logger.log(JSON.stringify(e)) and then look at the logs you will see that there is a lot of information available to you which removes the need to run extra functions to get things like a spreadsheet.
Use event objects
onEdit offers among others the event objects range and value which are helpful to retrieve the range that has been edited and its value.
Also
When you want to a cell and compare it against a value, like in if (currentCell = "YES") - you need to retrive its value (either currentCell.getValue() or just event.value) and you need to use == instead of = for comparison.
Be careful with getValues() vs getValue(). The former gives you a 2D array and is not necessary if you want to retrieve the value of a single cell.
There is no need to set your sheet to active in order to change its name.
You can rewrite your code as following:
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var currentCell = event.range;
var value = event.value;
if (value == "YES")
{
...
var sourceRow = range.getRowIndex();
var tabName = ss.getRange(sourceRow, 1).getValue();
...
ss.getSheetByName('Copy of CCTemplate').setName("CC" + tabName);
}
}
I am trying to copy and paste some data one after the others from one sheet to another with a google app script. The thing is that my script doesn't paste the data on the correct row. I would like it to paste the first data in cell A2. Then, when I run the script again, in cell A3 etc. But when I run the script, it pastes the data in cell A266
function Agregation () {
var spreadsheet = SpreadsheetApp.getActive();
var ss = spreadsheet.getSheetByName('Données');
var ts = spreadsheet.getSheetByName('Classement');
var lr = ts.getLastRow();
Logger.log(lr);
ss.getRange('A2:E7').copyTo(ts.getRange('A0'+(lr+1)), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
};
For documentation purposes.
The code was good, but the "Classement" sheet was not empty, since it had a non-empty cell at 'A265', the call to ts.getLastRow(); was returning 265. After clearing the sheet and running the code it was working as intended.
A way of moving forward could be to call ts.clear() before the copy is done to ensure the sheet is empty.
UPDATE
After testing, updated the code in the following way:
function Agregation() {
var spreadsheet = SpreadsheetApp.getActive();
var ss = spreadsheet.getSheetByName('Test données');
var ts = spreadsheet.getSheetByName('Test script');
var lr = ts.getLastRow();
Logger.log(lr);
ss.getRange('A2:E7').copyTo(ts.getRange('A'+(lr+1)), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
var toDelete = ts.getRange("A" + ts.getLastRow() - 3 + ":A" + ts.getLastRow()).getValues();
for (var i = 0; i < 4; i++){
if (toDelete[i][0] == ""){
Logger.log(i);
ts.deleteRow((ts.getLastRow() - 3) + i);
}
}
};
It adds the first line and then checks the next 3 to see if it is "", if so, it deletes the rows, so on the next run there wont be spaces
I have many rows that I want to count how many cells are bolded in a Google Sheets. I've looked through plugins and searched for pre-made code snippets (I have no programming skills).
I found a script that does exactly what I want it to do, but the cells it checks and where it outputs the answer is hard coded in located here (answer from user random-parts): Count Bold Cells in Google Sheets Script
I have several hundred rows that I want to know how many, if any, bolded cells, so I would have to make hundreds of separate scripts to use that. I've tried to turn it into a command I can drag/copy on the sheet but that skill is way beyond me.
This is the code that counts all the cells with bold, but the range input and output is hard coded in
var book = SpreadsheetApp.getActiveSpreadsheet();
var sheet = book.getActiveSheet();
var range_input = sheet.getRange("E2:S7");
var range_output = sheet.getRange("G14");
// Get the fontWeights of the range and flatten the array
var cell_styles = range_input.getFontWeights().join().split(",");
// Filter out any value that is not "bold"
var filter_bold = cell_styles.filter(function (e) { return e == "bold" });
// Set the count
range_output.setValue(filter_bold.length);
}
Can someone please provide a script and formula that would allow me to define a range in the sheet and then drag it and have it update?
Here is the link to a working example, and below is the code behind.
The important thing to note is that you need to refresh your browser window for formulas to re-calculate. The formula result cannot be refreshed by just changing the cells style.
function onOpen(){
refresh()
}
function refresh(){
var sheet = SpreadsheetApp.getActive().getSheetByName("Sheet2")
var formulaCol = sheet.getRange("E:E").getColumn()
var formulaRange = sheet.getRange(2, formulaCol, sheet.getDataRange().getLastRow()-1, 1)
var rowCount = formulaRange.getLastRow()-formulaRange.getRow()+1
var dummyFormulas = []
var formulas = formulaRange.getFormulas()
for(var i=0; i<rowCount; i++){
dummyFormulas.push(['=""'])
}
Logger.log(dummyFormulas)
Logger.log(formulas)
formulaRange.setFormulas(dummyFormulas)
SpreadsheetApp.flush()
formulaRange.setFormulas(formulas)
}
function countBoldCells(startRow, startColumn, endRow, endColumn, random){
var book = SpreadsheetApp.getActiveSpreadsheet();
var sheet = book.getActiveSheet();
var range_input = SpreadsheetApp.getActiveSheet().getRange(startRow, startColumn, endRow-startRow+1, endColumn-startColumn+1)
// Get the fontWeights of the range and flatten the array
var cell_styles = range_input.getFontWeights().join().split(",");
// Filter out any value that is not "bold"
var filter_bold = cell_styles.filter(function (e) { return e == "bold" });
return filter_bold.length;
}
I have poked around and found the following code that will advance to the last line on data in our Google Spreadsheet- at least until we add more lines beyond row 297.
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getSheets()[0].getRange("B297");
SpreadsheetApp.setActiveRange(range);
}
I am trying to figure out how to write this script so that it will go to the last line of data, regardless of the line number.
Is there a change that I can make to this code to accomplish this?
The method getLastRow() tells you the last row of a sheet that has data. This can be used to move the selection to that row. Another thing I changed in the sheet selection: your script always operates on the first sheet; it makes more sense to operate on whichever sheet is active currently.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange(sheet.getLastRow(), 1);
SpreadsheetApp.setActiveRange(range);
}
This can be placed into the spreadsheet menu using onOpen.
By the way, pressing Ctrl + ArrowDown does the same thing, if you do it in a column that has some data in every row (like the date or Id column).
The script below allows you to go to the last cell with the content of column A. It works even if some cells in the column A contain formulas.
Modifying the number in parentheses in lastRowOfCol(1) allows you to reach the last cell with content from another column.
Additionally, you can also change the focus to the first empty cell after the last one with content.
function onOpen(){
lastRowOfCol(1); //Enter the column number you want to use as the base of the search
}
function lastRowOfCol(column){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var total = sheet.getMaxRows();
var values = SpreadsheetApp.getActiveSheet().getRange(1,column,total).getValues();
for(var i=total-1;values[i]=="" && i>0; i--){}
var last = sheet.getRange(i+1,column);
//var last = sheet.getRange(i+1,1); //Option to fetch the last row of a given column, but to position in column 1
//var last = sheet.getRange(i+2,column); //Option to go to the cell below the last one with content
sheet.setActiveSelection(last);
}
Script from Marcelo Camargo in this forum
The currently relevant option that works on all non-hidden sheets, and not just on the active one:
function onOpen() {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const sheets = ss.getSheets()
const properties = PropertiesService.getDocumentProperties()
const lastActiveSheetName = properties.getProperty("lastActiveSheetName")
let lastActiveSheet
for (let sheet of sheets) {
if (!sheet.isSheetHidden()) {
const sheetName = sheet.getName()
const lastEdit = properties.getProperty(sheetName)
if (lastEdit) {
if (sheetName !== lastActiveSheetName){
sheet.getLastRow() // Without this magic does not work - I could not figure out the reasons
sheet.getLastColumn() // Without this magic does not work - I could not figure out the reasons
const [lastRow, lastCol] = lastEdit.split(',')
sheet.getRange(Number(lastRow), Number(lastCol)).activate() // With focus set to this cell
//sheet.setActiveSelection(sheet.getRange(Number(lastRow), Number(lastCol))) // Without setting focus to this cell
}
else {
lastActiveSheet = sheet
}
}
}
}
if(lastActiveSheet){
lastActiveSheet.getLastRow()
lastActiveSheet.getLastColumn()
const [lastRow, lastCol] = properties.getProperty(lastActiveSheetName).split(',')
lastActiveSheet.getRange(Number(lastRow), Number(lastCol)).activate()
}
}
function onEdit() {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const sheet = ss.getActiveSheet()
if (!sheet.isSheetHidden()) {
const cell = ss.getActiveCell()
const row = cell.getRow()
const column = cell.getColumn()
if (row !== 1 || column !== 1) { // Protection from the evil magic of "self-editing" the first cell
const sheetName = sheet.getName()
PropertiesService.getDocumentProperties().setProperty(sheetName, `${row},${column}`)
PropertiesService.getDocumentProperties().setProperty("lastActiveSheetName", sheetName)
}
}
}
PS: Please note that in the code I do not use a semicolon separator - it's more convenient for me.