Define whole column in API range specification - google-apps-script

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

Related

specific columns of selected row for print

How to set specific columns of selected row for print ? Google Apps Script - Google Sheets
Where I select a row of the google sheet, the data of these columns should be printed by the macro ("A", "B", "J", "S").
I've written this macro to select rows but I don't know how to make more changes required
function print() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getActiveSheet();
sheet.getRange(spreadsheet.getCurrentCell().getRow(), 1, 1, sheet.getMaxColumns()).activate();
}
I believe your goal is as follows.
When you select a cell, you want to retrieve the values of the specific columns of the row of the selected cell.
In this case, how about the following modification?
Modified script:
function print() {
var selectColumns = ["A", "B", "J", "S"]; // This is from your question.
var sheet = SpreadsheetApp.getActiveSheet();
var currentRow = sheet.getCurrentCell().getRow();
var columnIndex = selectColumns.map(letter => [...letter.toUpperCase()].reduce((c, e, i, a) => (c += (e.charCodeAt(0) - 64) * Math.pow(26, a.length - i - 1)), -1));
var values = sheet.getRange(currentRow, 1, 1, sheet.getMaxColumns()).getValues().map(r => columnIndex.map(c => r[c]))[0].join(",");
console.log(values) // Here, you can also confirm the values in the log.
Browser.msgBox(values); // You can see the values at a dialog of Spreadsheet.
}
When you select a cell and run this script, the values of the specific columns "A", "B", "J", "S" of the row of the selected cell are retrieved.
At columnIndex, the column letters are converted to the column indexes.
From sheet.getRange(spreadsheet.getCurrentCell().getRow(), 1, 1, sheet.getMaxColumns()).activate();, if you want to activate the specific columns, how about the following modification?
function print2() {
var selectColumns = ["A", "B", "J", "S"]; // This is from your question.
var sheet = SpreadsheetApp.getActiveSheet();
var currentRow = sheet.getCurrentCell().getRow();
sheet.getRangeList(selectColumns.map(e => e + currentRow)).activate();
}
Reference:
map()
Added 1:
From your following reply,
I mean when i using function print2() if selectColumns = ["A", "B", "C" ] , Cells A B C are highlighted And that's great. but when i try To Use CTRL + P and And I make the settings related to print the selected cells, I only see cell c in the print output
First, in this case, it is required to select the continuous ranges like "A1:C1". When the cells "A1:C1" and "E1" are selected, only "E1" is shown. And, when the cells are selected using RangeList, the cells are selected for every cell. I thought that this might be the reason for your current issue. So, when you want to use the selected cells using the printer setting, how about the following sample script?
Sample script:
When you use this script, please set startColumn and endColumn. And, please select a cell. And, please run the script. By this, in this sample, the columns "A" to "C" are selected. When you push CTRL + P and do the settings related to print the selected cells, you can see the 3 cells.
function print3() {
var startColumn = "A"; // Please set the start column.
var endColumn = "C"; // Please set the end column.
var sheet = SpreadsheetApp.getActiveSheet();
var currentRow = sheet.getCurrentCell().getRow();
sheet.getRange(`${startColumn}${currentRow}:${endColumn}${currentRow}`).activate();
}
Added 2:
If you want to use the discrete columns and cells, how about the following sample script?
Sample script:
This script uses Sheets API. So, please enable Sheets API at Advanced Google services.
When you use this script, please set selectColumns. In this case, you can set the discrete columns. And, please select a cell, and run the script of print4. By this, only the columns of selectColumns of the selected row are shown. By this, when you push CTRL + P, you can see the showing cells. This is another workaround for achieving your goal.
After your work is finished, when you run showAll, all rows and columns are shown.
function print4() {
var selectColumns = ["A", "B", "J", "S"]; // This is from your question.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var currentRow = sheet.getCurrentCell().getRow();
var columnIndex = selectColumns.map(letter => [...letter.toUpperCase()].reduce((c, e, i, a) => (c += (e.charCodeAt(0) - 64) * Math.pow(26, a.length - i - 1)), -1));
var sheetId = sheet.getSheetId();
var requests = [...Array(sheet.getMaxColumns())].reduce((ar, _, i) => {
if (!columnIndex.includes(i)) {
ar.push({ updateDimensionProperties: { range: { sheetId, startIndex: i, endIndex: i + 1, dimension: "COLUMNS" }, properties: { hiddenByUser: true }, fields: "hiddenByUser" } });
}
return ar;
}, []);
var maxRows = sheet.getMaxRows();
if (currentRow == 1) {
requests.push({ updateDimensionProperties: { range: { sheetId, startIndex: currentRow, endIndex: maxRows, dimension: "ROWS" }, properties: { hiddenByUser: true }, fields: "hiddenByUser" } });
} else if (currentRow == maxRows) {
requests.push({ updateDimensionProperties: { range: { sheetId, startIndex: 0, endIndex: maxRows - 1, dimension: "ROWS" }, properties: { hiddenByUser: true }, fields: "hiddenByUser" } });
} else {
requests.push({ updateDimensionProperties: { range: { sheetId, startIndex: 0, endIndex: currentRow - 1, dimension: "ROWS" }, properties: { hiddenByUser: true }, fields: "hiddenByUser" } });
requests.push({ updateDimensionProperties: { range: { sheetId, startIndex: currentRow, endIndex: maxRows, dimension: "ROWS" }, properties: { hiddenByUser: true }, fields: "hiddenByUser" } });
}
Sheets.Spreadsheets.batchUpdate({ requests }, ss.getId());
}
function showAll() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
const requests = [{ updateDimensionProperties: { range: { sheetId: sheet.getSheetId(), dimension: "COLUMNS" }, properties: { hiddenByUser: false }, fields: "hiddenByUser" } }, { updateDimensionProperties: { range: { sheetId: sheet.getSheetId(), dimension: "ROWS" }, properties: { hiddenByUser: false }, fields: "hiddenByUser" } }];
Sheets.Spreadsheets.batchUpdate({ requests }, ss.getId());
}

Conditional borders troubleshooting (Google sheets)

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

Add multiple filterviews using values from a column of the sheet

I have this Apps Script function that Adds a filterview to a sheet. I would like to use a function that creates multiple filterviews to the same sheet, using the values from columnIndex 3 to define the title, filterViewId, and filter criteria.
This is the Apps Script function that I have:
function AddFilterView() { //Adds a filterview
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheet = ss.getSheetByName('sheet1'); //Sheet in which filterview will be added
var lastRow = dataSheet.getLastRow();
var lastColumn = dataSheet.getLastColumn();
var sheetId = dataSheet.getSheetId();
var filterSettings = {
"filterViewId": "2", // write a integer+32 number for the filterview Id
"title": "2", // write a title for the filterview
"range":{
"sheetId": sheetId,
"startRowIndex": 0,
"endRowIndex": lastRow,
"startColumnIndex": 0,
"endColumnIndex": lastColumn
}
};
var condition_value = {
"type": "TEXT_EQ",
"values": [{"userEnteredValue": "2"}]
};
filterSettings.criteria = {};
var columnIndex = 3; // column that defines criteria [A = 0]
filterSettings['criteria'][columnIndex] = {
'condition' : condition_value
};
var requests = [{
"addFilterView":{
"filter": filterSettings,
}
}];
Sheets.Spreadsheets.batchUpdate({"requests":requests}, ss.getId());
}
And this is a image capture of the sheet that I am using:

Exporting sheet data to JSON file

This is my simple Sheet divided by freezed rows to Head and Body:
And this is what the final output(json) should look like:
{
"3":{
"AB1":{
"A2":"A3",
"B2":"B3"
},
"C1":{
"C2":"C3"
}
},
"4":{
"AB1":{
"A2":"A4",
"B2":"B4"
},
"C1":{
"C2":"C4"
}
},
...
}
My code look like this:
function doGet() {
var SpreadSheet = SpreadsheetApp.getActiveSpreadsheet();
var Sheet = SpreadSheet.getSheets()[1];
var FirstRow = 1;
var FirstColumn = 1;
var LastRow = Sheet.getLastRow();
var LastColumn = Sheet.getLastColumn();
var FrozenRows = Sheet.getFrozenRows();
var FrozenColumns = Sheet.getFrozenColumns();
var HeadRange = Sheet.getRange(FirstRow, FirstColumn + FrozenColumns, FrozenRows - FirstRow + 1, LastColumn - FrozenColumns); // A1:C2
var HeadData = HeadRange.getValues(); // [[AB1, , C1], [A2, B2, C2]]
var BodyRange = Sheet.getRange(FirstRow + FrozenRows, FirstColumn + FrozenColumns, LastRow - FrozenRows, LastColumn - FrozenColumns); // A3:C6
var BodyData = BodyRange.getValues(); // [[A3, B3, C3], [A4, B4, C4], [A5, B5, C5], [A6, B6, C6]]
and will end with this:
var OutputData = ContentService.createTextOutput(JSON.stringify(InputData)).setMimeType(ContentService.MimeType.JSON);
return OutputData;
and now my problems :), first problem is, how get value from empty merged cell, when don't know his range, only know other side when know range and want value
for (var i = 0; i < HeadData[0].length; i++) {
var Category = HeadData[0][i];
var CellValue = (RangeCell.isPartOfMerge() ? RangeCell.getMergedRanges()[0].getCell(1,1) : RangeCell).getValue();
Second problem is, how put code together when want start json with number of row data, then category, subcategory and last with item data:
var Obj = {};
for (var i = 1; i <= ItemsRange.getNumRows(); i++) {
var ItemIndex = ItemsRange.getCell(i,1).getRowIndex();
for (var j = 0; j < BodyData.length; j++) {
for (var k = 0; k < BodyData[j].length; k++) {
var ItemCell = BodyData[j][k];
}
}
Obj[ItemIndex] = {};
}
In this case, how about the following flow?
Retrieve values from Spreadsheet.
This is from your script.
Recreate header rows.
Create the result object.
Modification points:
In this modification, at first, the header data is created. The merged ranges can be retrieve by getMergedRanges(). I created the header data using this.
When your sample Spreadsheet is used, HeadData becomes [[AB1, AB1, C1], [A2, B2, C2]] from [[AB1, , C1], [A2, B2, C2]].
In your case, it has already been found that the result object is the nested object with 3 levels. I think that this can be used.
Modified script:
function doGet() {
// 1. Retrieve values from Spreadsheet. This is from your script.
var SpreadSheet = SpreadsheetApp.getActiveSpreadsheet();
var Sheet = SpreadSheet.getSheets()[1];
var FirstRow = 1;
var FirstColumn = 1;
var LastRow = Sheet.getLastRow();
var LastColumn = Sheet.getLastColumn();
var FrozenRows = Sheet.getFrozenRows();
var FrozenColumns = Sheet.getFrozenColumns();
var HeadRange = Sheet.getRange(FirstRow, FirstColumn + FrozenColumns, FrozenRows - FirstRow + 1, LastColumn - FrozenColumns); // A1:C2
var HeadData = HeadRange.getValues(); // [[AB1, , C1], [A2, B2, C2]]
var BodyRange = Sheet.getRange(FirstRow + FrozenRows, FirstColumn + FrozenColumns, LastRow - FrozenRows, LastColumn - FrozenColumns); // A3:C6
var BodyData = BodyRange.getValues(); // [[A3, B3, C3], [A4, B4, C4], [A5, B5, C5], [A6, B6, C6]]
// 2. Recreate header rows.
Sheet.getRange(1, 1, 1, Sheet.getLastColumn()).getMergedRanges().forEach(r => {
let temp = "";
for (let i = r.getColumn(); i <= r.getNumColumns(); i++) {
if (HeadData[0][i - 1].toString() != "") temp = HeadData[0][i - 1];
}
for (let i = r.getColumn(); i <= r.getNumColumns(); i++) {
HeadData[0][i - 1] = temp;
}
});
// 3. Create the result object.
const InputData = BodyData.reduce((o1, r, i) => {
o1[i + FirstRow + FrozenRows] = r.reduce((o2, c, j) => {
const t1 = HeadData[0][j];
const t2 = {[HeadData[1][j]]: c};
return Object.assign(o2, {[t1]: o2[t1] ? Object.assign(o2[t1], t2) : t2});
}, {});
return o1;
}, {});
var OutputData = ContentService.createTextOutput(JSON.stringify(InputData)).setMimeType(ContentService.MimeType.JSON);
return OutputData;
}
Result:
When your sample Spreadsheet is used, the value of InputData is as follows.
{
"3": {
"AB1": {
"A2": "A3",
"B2": "B3"
},
"C1": {
"C2": "C3"
}
},
"4": {
"AB1": {
"A2": "A4",
"B2": "B4"
},
"C1": {
"C2": "C4"
}
},
"5": {
"AB1": {
"A2": "A5",
"B2": "B5"
},
"C1": {
"C2": "C5"
}
},
"6": {
"AB1": {
"A2": "A6",
"B2": "B6"
},
"C1": {
"C2": "C6"
}
}
}
Note:
When you modified the script of Web Apps, please redeploy the Web Apps as new version. By this, the latest script is reflected to the Web Apps. Please be careful this.
When your actual situation is largely different from your sample Spreadsheet in your question, this modified script might not work. So please be careful this.
Please use this modified script with V8.
References:
getMergedRanges()
reduce()
Object.assign()

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