I've been using the script below to copy and paste all previous rows in an inserted row before on Google Sheets.
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('20:20').activate();
spreadsheet.getActiveSheet().insertRowsBefore(spreadsheet.getActiveRange().getRow(), 1);
spreadsheet.getActiveRange().offset(0, 0, 1, spreadsheet.getActiveRange().getNumColumns()).activate();
spreadsheet.getRange('21:21').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
spreadsheet.getRange('L20').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
spreadsheet.getRange('H20').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
};
As I will have insertions below each other, whenever I use the top ones will change the line to be copied to the others. I would like to remove dependency on expression ('20:20'), and a think that NamedRange is the way.
Is it possible to copy the values of a whole row or named range in one sheet and paste them in the same sheet, just creating a line above?
Using the Porperties Service [1] you can fix your problem, I edited the code linked to your example Spreadsheet as follows:
function PlusMP() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('5:5').activate();
spreadsheet.getActiveSheet().insertRowsBefore(spreadsheet.getActiveRange().getRow(), 1);
spreadsheet.getActiveRange().offset(0, 0, 1, spreadsheet.getActiveRange().getNumColumns()).activate();
spreadsheet.getRange('6:6').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
spreadsheet.getRange('L5').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
spreadsheet.getRange('H5').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
//Set offset property
var scriptProperties = PropertiesService.getScriptProperties();
var offset = Number(scriptProperties.getProperty('offset'));
if ( offset == null) {
scriptProperties.setProperty('offset', 1);
}
else {
scriptProperties.setProperty('offset', offset + 1);
}
};
function PlusCOMP() {
//Get offset property
var scriptProperties = PropertiesService.getScriptProperties();
var offset = Number(scriptProperties.getProperty('offset'));
if(offset == null) {
offset = 0;
}
var row = 10 + offset;
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange(row + ':' + row).activate();
spreadsheet.getActiveSheet().insertRowsBefore(spreadsheet.getActiveRange().getRow(), 1);
spreadsheet.getActiveRange().offset(0, 0, 1, spreadsheet.getActiveRange().getNumColumns()).activate();
spreadsheet.getRange(row + ':' + row).copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
spreadsheet.getRange('L' + row).activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
spreadsheet.getRange('H' + row).activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
};
function resetOffset() {
PropertiesService.getScriptProperties().setProperty('offset', 0);
}
When the PlusMP function is run it'll set or update the offset property, and when PlusCOMP function is run it'll use the same offset property to insert the rows accordingly.
[1] https://developers.google.com/apps-script/reference/properties/properties
Related
I'm trying to set up a macro to run once a month to collect current figures from a set of formulas, but I'm struggling to get it to select the right sheet before running the copy and paste commands.
function RecordCurrentFigures() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getSheetByName('Current Figures');
spreadsheet.getRange('9:9').activate();
spreadsheet.getActiveSheet().insertRowsBefore(spreadsheet.getActiveRange().getRow(), 1);
spreadsheet.getActiveRange().offset(0, 0, 1, spreadsheet.getActiveRange().getNumColumns()).activate();
spreadsheet.getRange('B9').activate();
spreadsheet.getRange('B5:F5').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_FORMAT, false);
spreadsheet.getRange('B5:F5').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
};
I think the issue is that you are calling it but you're not using it afterwards, you are using getActiveSheet and getActiveRange instead.
Try:
function RecordCurrentFigures() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getSheetByName('Current Figures');
var range = sheet.getRange('9:9')
sheet.insertRowsBefore(range.getRow(), 1);
// range.offset(0, 0, 1, range.getNumColumns()); // this is a NOP
var range2 = sheet.getRange('B9');
sheet.getRange('B5:F5').copyTo(range2, SpreadsheetApp.CopyPasteType.PASTE_FORMAT, false);
sheet.getRange('B5:F5').copyTo(range2, SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
}
I would like to add borders to cells in a Google Sheet using conditional formatting. I am aware that you cannot do this using the standard conditional formatting process in Google Sheets so I'm trying to get to grips with how to do it using a script.
I have copied a script from the following solution, and attempted to edit it for my needs: (Add border format to row if condition met in Google Sheets)
However, I am still coming to terms with how these scripts work and haven't yet been able to make this work as desired.
The desired effect is that for all rows 5 and higher, where A is not null, a border should be applied to all cells in columns A to M. The sheet is called 'Kit check list', and the script should be triggered any timean edit is made to the sheet.
Here is the my attempt so far
function onEdit() {
GroupMyData(); // trigger this function when edits are made
}
function GroupMyData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Kit check list'); // apply to sheet name only
var rows = sheet.getRange('A5:M'); // range to apply formatting to
var numRows = rows.getNumRows(); // no. of rows in the range named above
var values = rows.getValues(); // array of values in the range named above
var testvalues = sheet.getRange('a5:a').getValues(); // array of values to be tested (1st column of the range named above)
rows.setBorder(false, false, false, false, false, false, "black", SpreadsheetApp.BorderStyle.SOLID); // remove existing borders before applying rule below
//Logger.log(numRows);
for (var i = 0; i <= numRows - 1; i++) {
var n = i + 1;
//Logger.log(n);
//Logger.log(testvalues[i] > 0);
//Logger.log(testvalues[i]);
if (testvalues[i] > 0) { // test applied to array of values
sheet.getRange('a' + n + ':m' + n).setBorder(true, true, true, true, true, true, "black", SpreadsheetApp.BorderStyle.SOLID); // format if true
}
}
};
Unfortunately all it only resets the borders in the specified area, and does not apply a borders to the desired rows.
Any help with this would be much appreciated.
Try this:
function onEdit(e) {
const sh = e.range.getSheet();
if (sh.getName() == 'Kit check list') {
const sr = 5;
const rg = sh.getRange(sr, 1, sh.getLastRow() - sr + 1, sh.getLastColumn());
const vs = rg.getValues();
rg.setBorder(false, false, false, false, false, false, "black", SpreadsheetApp.BorderStyle.SOLID);
const numcolumns = sh.getLastColumn();
vs.forEach((r, i) => {
if (r[0]) {
sh.getRange(i + sr, 1, 1, numcolumns).setBorder(true, true, true, true, true, true, "black", SpreadsheetApp.BorderStyle.SOLID);
}
});
}
}
Demo:
Note: you cannot run this function without providing the event object which populates the e. The only reasonable way to test it is to set it up and save it and edit the sheet.
You might actually like it better this way:
function onEdit(e) {
const sh = e.range.getSheet();
if (sh.getName() == 'Kit check list') {
const sr = 5;
const rg = sh.getRange(sr, 1, sh.getLastRow() - sr + 1, sh.getLastColumn());
const vs = rg.getValues();
//rg.setBorder(false, false, false, false, false, false, "black", SpreadsheetApp.BorderStyle.SOLID);
const numcolumns = sh.getLastColumn();
vs.forEach((r, i) => {
if (r[0]) {
sh.getRange(i + sr, 1, 1, numcolumns).setBorder(true, true, true, true, true, true, "black", SpreadsheetApp.BorderStyle.SOLID);
} else {
sh.getRange(i + sr, 1, 1, numcolumns).setBorder(false, false, false, false, false, false, "black", SpreadsheetApp.BorderStyle.SOLID);
}
});
}
}
Your script works fine. I just fixed one line.
Instead of this:
var n = i + 1;
You need:
var n = i + 5;
Here is the code:
function onEdit() {
GroupMyData(); // trigger this function when edits are made
}
function GroupMyData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Kit check list'); // apply to sheet name only
var rows = sheet.getRange('A5:M'); // range to apply formatting to
var numRows = rows.getNumRows(); // no. of rows in the range named above
var values = rows.getValues(); // array of values in the range named above
var testvalues = sheet.getRange('A5:A').getValues(); // array of values to be tested (1st column of the range named above)
rows.setBorder(false, false, false, false, false, false, "black", SpreadsheetApp.BorderStyle.SOLID); // remove existing borders before applying rule below
for (var i=0; i <= numRows-1; i++) {
var n = i + 5;
//Logger.log(n);
//Logger.log(testvalues[i] > 0);
//Logger.log(testvalues[i]);
if (testvalues[i] > 0) { // test applied to array of values
sheet.getRange('A' + n + ':M' + n).setBorder(true, true, true, true, true, true, "black", SpreadsheetApp.BorderStyle.SOLID); // format if true
}
}
};
without any script, you can do it by first using conditional formatting in MS Excel, then importing the workbook into google sheets ! weird ...
I am new to Google Apps Script script writing and trying to self teach by starting off making a generic table. So far I have:
made a header;
changed the background;
changed the font;
added data validation;
added thick outside borders as well as other borders
and keeping a maximum of five rows and columns at any one time
My question is:
How do I remove empty rows in a data range? I have tried multiple methods so far but I like to have the top row as a border so don't want to delete that. This is the script I have written so far
function formatScriptLearning1() {
var ScriptLearning = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Script learning 1");
var Header = ScriptLearning.getRange("B2").getDataRegion(SpreadsheetApp.Dimension.COLUMNS);
var LastRow = ScriptLearning.getLastRow();
var MaxRows = ScriptLearning.getMaxRows();
var LastColumn = ScriptLearning.getLastColumn();
var MaxColumns = ScriptLearning.getMaxColumns();
var Range = ScriptLearning.getRange(2, 2, LastRow - 1, LastColumn - 1);
var Outside = Range
var DataVal = ScriptLearning.getRange(3, 7, LastRow - 1, 1);
var validation = SpreadsheetApp.newDataValidation().requireValueInList(['Yes', 'No'], true).build();
var RemainingRows = ScriptLearning.getRange(LastRow + 1, 7, MaxRows);
var RemainingColumns = ScriptLearning.getRange(2, LastColumn + 1, 1, 5);
Range.setBorder(true, true, true, true, true, true, null, SpreadsheetApp.BorderStyle.SOLID);
Outside.setBorder(true, true, true, true, true, null, null, SpreadsheetApp.BorderStyle.SOLID_MEDIUM);
Header.setBorder(true, true, true, true, true, true, true, SpreadsheetApp.BorderStyle.SOLID_MEDIUM);
Header.setFontSize(11).setBackground("#ea9999").setFontWeight("Bold").setFontLine("Underline").setFontFamily("Georgia");
if (MaxRows - LastRow != 0) {
ScriptLearning.deleteRows(LastRow + 1, MaxRows - LastRow);
}
if (MaxColumns - LastColumn != 0) {
ScriptLearning.deleteColumns(LastColumn + 1, MaxColumns - LastColumn);
}
DataVal.setDataValidation(validation);
ScriptLearning.insertRowsAfter(LastRow, 5);
ScriptLearning.insertColumnsAfter(LastColumn, 5);
RemainingRows.clearDataValidations().clearFormat();
RemainingColumns.clear();
Any help would be greatly appreciated!
function removeEmptyRows() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getActiveSheet();
const rg=sh.getDataRange();
const vs=rg.getValues();
let d=0;
vs.forEach(function(r,i){if(r.join('').length==0) {sh.deleteRow(i+1-d++);}});
}
The script deletes all values in the selected cells. I would like to write in exceptions if the value is something I don't want to be deleted.
Haven't tried anything because I'm not that good.
function Gtown() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('O38:P39').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
spreadsheet.getRange('A38:B39').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
spreadsheet.getRange('O26:P27').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
spreadsheet.getRange('I26:J27').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
spreadsheet.getRange('G26:H27').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
spreadsheet.getRange('A26:B27').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
spreadsheet.getRange('O16:P17').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
spreadsheet.getRange('I16:J17').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
spreadsheet.getRange('G16:H17').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
spreadsheet.getRange('A16:B17').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
spreadsheet.getRange('O6:P7').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
spreadsheet.getRange('I6:J7').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
spreadsheet.getRange('G6:H7').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
spreadsheet.getRange('A6:B7').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
};
No messages. I only want to delete certain values.
First of all, please, use a loop! Right now your script is very "heavy":
function Gtown() {
var ss = SpreadsheetApp.getActive();
var rl = ss.getRangeList(['A1:A5','B1:B5']); //put any Range references here;
var rs = rl.getRanges();
var opts = {contentsOnly: true, skipFilteredRows: true};
rs.forEach(function(range){
range.clear(opts);
});
}
Then, inside the forEach "loop", instead of unconditional cleanup, wrap clear() method in an if...else statement (Range values can be accessed via getValue() or getValues() method calls)
Try this:
function clearSelectedLocations() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var rA=['O38:P39','A38:B39','O26:P27','I26:J27','G26:H27','A26:B27','O16:P17','I16:J17','G16:H17','A16:B17','O6:P7','I6:J7','G6:H7','A6:B7'];
var rgA=sh.getRangeList(rA).getRanges();
for(var i=0;i<rgA.length;i++) {
rgA[i].clear({contentsOnly: true, skipFilteredRows: true});
}
}
Try this:
function clearSelectedLocations() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var rg=sh.getDataRange();
var vA=rg.getValues()
for(var i=0;i<vA.length;i++) {
if(sh.isRowHiddenByFilter(i+1)) {
continue;
}else{
for(var j=0;j<vA[i].length;j++) {
if(vA[i][j].toString()=="On Duty") {
sh.getRange(i+1,j+1).clear({contentsOnly: true});
}
}
}
}
If you have more than one values to clear:
function clearSelectedLocations() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var rg=sh.getDataRange();
var vA=rg.getValues();
var clA=["On Duty"];
for(var i=0;i<vA.length;i++) {
if(sh.isRowHiddenByFilter(i+1)) {
continue;
}else{
for(var j=0;j<vA[i].length;j++) {
if(clA.indexOf(vA[i][j].toString())>-1) {
sh.getRange(i+1,j+1).clear({contentsOnly: true});
}
}
}
}
I would like to know how to clear the contents of all lines that are being hidden through this code, specifically in the two IFS.
I tried to include this excerpt:
ss.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});}
but it is only cleaning the contents of the selected cell itself
function remcausa() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var cell=sh.getActiveCell();
var row=cell.getRow();
if (row<47) {
sh.hideRows(row + 0, 4);ss.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});}
else { if(row>50) {sh.hideRows(row -1, 4)};
ss.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});}
}
the expected result would be to clear the contents of all the lines being hidden.
Thanks
How about this?
function remcausa() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var cell=sh.getActiveCell();
var row=cell.getRow();
if (row<47) {
sh.hideRows(row, 4);
sh.getRange(row,1,4,sh.getLastColumn()).clearContent();
}else if(row>50) {
sh.hideRows(row-1, 4);
sh.getRange(row-1,1,4,sh.getLastColumn()).clearContent();
}
}