Conditional borders troubleshooting (Google sheets) - google-apps-script

I have a dataset with four different treatment types in blocks of 5 or 7 rows. I want to add borders between each treatment set, so I tried modifying Tedinoz's code. The problem is that this code only adds a border to unique values, and I want to add a border whenever the value is different from the previous row. How can I modify the if statement if (Treatments.indexOf(row) == -1) to search instead for rows whose value for 'Treatment' is different from the previous row?
Here is the full code:
function lineBetween() {
//setup spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
// get key variables
var LastRow = 417
var LastColumn = sheet.getLastColumn();
var NumColumns = 8;
// allow for headers
var headerRows = 1;
//erase any current formatting
var ClearRange = sheet.getRange(1, 1, LastRow, NumColumns).setBorder(false, false, false, false, false, false); // clear all formatting
// get the data
var data = sheet.getRange((+1 + headerRows), 1, (LastRow - headerRows), LastColumn).getValues();
// setup new array
var Treatments = new Array();
// Loop through treatments(Column C)
for (var i in data) {
var row = data[i][2].toString();
// Logger.log("Inside LOOP: i = "+i+", value = "+ row);// DEBUG
// search for unqiue values
if (Treatments.indexOf(row) == -1) { // if value =-1, then the variable is unique
// Logger.log("Inside IF#1: i = "+i+", "+row+" is not referenced. Adding it");//DEBUG
// underline the previous row
if (i != 0) {
// This IF statement to avoid underlining the Header row
var range = sheet.getRange((+i + 1 + headerRows), 1, 1, NumColumns).setBorder(true, false, false, false, false, false, "black", SpreadsheetApp.BorderStyle.SOLID); // format if true
}
// continue to build array
Treatments.push(row);
}
}
// underline the last row of the treatments column
var range = sheet.getRange(LastRow, 1, 1, NumColumns).setBorder(null, null, true, null, false, false, "black", SpreadsheetApp.BorderStyle.SOLID_MEDIUM); // format if true
//Logger.log(Treatments);// DEBUG
}
[1]: https://stackoverflow.com/questions/53053492/conditional-borders-in-google-sheets

Try this:
function lineBetween() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sh = ss.getSheetByName("Sheet1");
sh.getDataRange().setBorder(false, false, false, false, false, false); // clear all formatting
let sr = 2;//data start row
const data = sh.getRange(sr, 1, sh.getLastRow() - sr + 1, sh.getLastColumn()).getValues();
data.forEach((r, i, arr) => {
if (i > 0) {
if (r[2] != arr[i - 1][2]) {
sh.getRange(i + sr, 1, 1, sh.getLastColumn()).setBorder(true, true, true, true, false, false);
}
}
});
}
Actually I think this works better for me:
function lineBetween() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sh = ss.getSheetByName("Sheet0");
sh.getDataRange().setBorder(false, false, false, false, false, false); // clear all formatting
let sr = 2;//data start row
const data = sh.getRange(sr, 1, sh.getLastRow() - sr + 1, sh.getLastColumn()).getValues();
let uA = [];
let dA = [];
data.forEach((r, i, arr) => {
if (i > 0) {
if (r[2] != arr[i - 1][2]) {
sh.getRange(i + sr, 1, 1, sh.getLastColumn()).setBorder(true, false, false, false, false, false);
}
}
});
}

Related

Gsheet - Check last empty row and set data as array per row

I do not know how to optimize this. I just want to check the last empty row and paste the array data horizontally. This is my current setup where I manually set the location of each row. I tried using .setValues and [[row[1], row[2]] but the problem is I am only referencing 1 row with getLastRow().
const sourceData = source.getDataRange().getValues().slice(1);
sourceData.forEach((row, i)=> {
if(row[0] == "") {
return;
} else {
let id = row[0];
var database = SpreadsheetApp.openById(id);
var copyToSheet = database.getSheetByName("Sheet1");
var lastRow = copyToSheet.getLastRow();
copyToSheet.getRange(lastRow + 1, 1).setValue(row[2]);
copyToSheet.getRange(lastRow + 1, 2).setValue(row[3]);
copyToSheet.getRange(lastRow + 1, 3).setValue(row[4]);
}
});
Try this:
function lfunk() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");
const vs = sh.getDataRange().getValues().slice(1);
vs.forEach((row, i) => {
if (row[0]) {
var ssi = SpreadsheetApp.openById(row[0]);
var shi = ssi.getSheetByName("Sheet1");
sh1.getRange(shi.getLastRow() + 1, 1, 1, 3).setValues([[row[2], row[3], row[4]]])
}
});
}
This will still potentially take a while because you have to wait to open up all of the spreadsheets

Conditional formatting of borders in Google Sheets using Apps Script

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 ...

Deleting empty rows within a table

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++);}});
}

Borders when row has data but no border if empty

I have a sheet that I want to dynamically add borders to filter results. I have it working where it adds the borders, but when i select another filter it keeps the rows with previous data borders. So if data set is smaller it has all empty borders under it. How would I fix this with this script?
function onEdit(ss) {
var classeur = SpreadsheetApp.getActiveSpreadsheet();
var ss = classeur.getActiveSheet();
var range = ss.getRange("A5:a"); // Modified
range.setBorder(false, false, false, false, false, false);
var values = range.getValues();
var offsetRow = range.getRowIndex(); // Added
for (var i = 0; i < values.length; i++) {
if (values[i][0]) { // Modified
ss.getRange("A" + (i + offsetRow) + ":P" + (i + offsetRow))
.setBorder(true, true, true, true, true, true, "black",
SpreadsheetApp.BorderStyle.SOLID_MEDIUM) // Modified
.setBackground('#FFFFFF');
}
}
}
Try this:
function onEdit(e) {
var sh=e.range.getSheet();
sh.getRange(1,1,sh.getMaxRows(),sh.getMaxColumns()).setBorder(false, false, false, false, false, false);
var range = sh.getRange(5,1,sh.getLastRow()-4,1); // Modified
range.setBorder(false, false, false, false, false, false);
var values = range.getValues();
for (var i=0;i<values.length;i++) {
if (values[i][0]) { // Modified
sh.getRange(i+5,1,1,16).setBorder(true, true, true, true, true, true, "black",SpreadsheetApp.BorderStyle.SOLID_MEDIUM).setBackground('#FFFFFF');
}
}
}
You cannot call this function from the script editor unless you supply the event object.

Define whole column in API range specification

I can use below syntax to refer whole column A,B and C:
A1:C
Below script will refer to full available cells!
var myRange = {
'sheetId': sheet.getSheetId(),
'startRowIndex': 0,
'endRowIndex': sheet.getLastRow(),
'startColumnIndex': 0,
'endColumnIndex': sheet.getLastColumn()
}
If user insert a new line, this range will not cover it. How to change it to support whole column just like A1:C?
Full script as below:
function addConditonalFormat() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getActiveSheet()
sheet.clearConditionalFormatRules()
var colorMerged = {'red': 222/255, 'green': 235/255, 'blue': 246/255, 'alpha': 0.7}
var colorSkipped = {'red': 222/255, 'green': 235/255, 'blue': 0, 'alpha': 0.7}
var myRange = {
'sheetId': sheet.getSheetId(),
'startRowIndex': 0,
'endRowIndex': sheet.getLastRow(),
'startColumnIndex': 0,
'endColumnIndex': sheet.getLastColumn()
}
var config = [["merged",colorMerged],["skipped",colorSkipped]]
var requests = []
for (var i=0;i<config.length;i++) {
var row = config[i]
var keyword = row[0]
var color = row[1]
Logger.log(keyword + ":" + color)
var cond = {'addConditionalFormatRule': {
'index': 0,
'rule': {
'ranges': [ myRange ],
'booleanRule': {
'format': {'backgroundColor': color},
'condition': {
'type': 'CUSTOM_FORMULA',
'values':[{'userEnteredValue': '=$A:$A="' + keyword + '"'}]},},},} }
requests.push(cond)
}
var format_req = {
'requests': requests,
'includeSpreadsheetInResponse': false,
}
Sheets.Spreadsheets.batchUpdate(JSON.stringify(format_req), ss.getId())
}
After run the script, then insert rows after last row, the conditional format will not apply to the new added rows!
Simple: to refer to the entirety of the sheet, do not supply any index specifications. Per the DimensionRange and GridRange documentation, missing indices indicate an unbounded specification.
const theWholeSheet = {
sheetId: sheet.getSheetId()
};
const noFirstRowOrFirstCol = {
sheetId: sheet.getSheetId(),
startColumnIndex: 1,
startRowIndex: 1
};