I've got a Google App Script which is copying rows from one sheet to another, performing various transformations. This logic ultimately gets rows onto the new sheet using sheet.appendRow(row detail). I would like these newly created rows to have a background colour (my intention is to hold a 'latestColour' so I can alternate the shading).
So, is there anyway to add shading within the appendRow method itself, or easily determine the range that the appendRow method processed, such that I can apply additional logic to add the shading.
You can use conditional formatting
=and(A1<>"",A2="")
Although I'm not sure whether I could correctly understand your situation, from your question, I thought that you might be using [Format] --> [Alternating colors] in Google Spreadsheet. And, when a new row is appended by putting the values, you might want to reflect "Alternating colors" in the appended row. If my guess is correct, how about the following sample script?
Sample script:
function myFunction() {
const addValues = ["sample1", "sample2", "sample3"]; // This is a sample appending value. Please replace this for your value.
const sheetName = "Sheet1"; // Please set the sheet name.
// Retrieve banding object from the data range.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const b = sheet.getDataRange().getBandings();
if (b.length == 0) {
console.log("Bandings are not used.");
return;
}
// Append the value.
sheet.appendRow(addValues);
// Expand the range of banding.
b[0].setRange(sheet.getDataRange());
}
When this script is run, the current banding is retrieved. And, after the value was appended, the banding is updated by including the appended row. In this sample, even when the multiple rows are appended, this script can be used.
Note:
From your question, I guessed that there is one banding in the data range in your sheet. Please be careful this.
References:
getBandings()
setRange(range)
Unfortunately the method appendRow() does not receive formatting settings as input, only an array of values.
However, here is a suggestion if you want to implement your own logic:
Sample code:
function applyColorLastRow() {
var ss = SpreadsheetApp.getActive(); //get active sheets file
var range = ss.getDataRange(); //get populated range, you may want to set a range manually if needed.
var lastRowNum = range.getLastRow(); //getting the last row index of the range.
var lastRowRange = ss.getRange(`${lastRowNum}:${lastRowNum}`); //narrowing the range (using A1 notation) to the last row only to apply color
var lastRowColor = lastRowRange.getCell(1,1).getBackgroundObject().asRgbColor().asHexString();
//Your row coloring logic here...
if (lastRowColor === '#ffffff'){ //toggling white/grey color as an example...
lastRowRange.setBackground('#cccccc'); //apply grey color to all cells in the last row range
} else {
lastRowRange.setBackground('#ffffff'); //apply white color to all cells in the last row range
};
}
I've tried making a Google Apps Script, but I was having trouble trying to understand how to set it up. From this it seems like I can create a function that I can call inside the spreadsheet itself like the SUM function provided by Google Sheets. I've taken a look at the getBackground() function, but it seems like it needs some global variables included instead of just functions.
Here's my current spreadsheet:
I want to input a function where it takes in the ranges A2:A1000 and based on the background color of the cell, determine whether it goes into "Work" or "Life" and then adds it onto the cells E4 (Total Work) or F4 (Total Life) accordingly. The cells in column A will always be numbers.
Here's what I've tried, I think I may be off the path completely based off of my single cell approach:
function workTime(input) {
if (input.getBackground() == "#d9ead3") {
input.setFontColor(4285f4)
} else {
input.setFontColor(null)
}
}
//I get errors on line 3 for some reason though...
TL;DR Based on the background colors of the cells, how do I create a function that calculates the sum of the numbers in those specific colors and displays them in different cells under the "Total Work Time" and "Total Life Time" accordingly?
The "custom formula" approach is very limited
The only input you'll get into the custom formulae are the values, not the cell object. The function that is running the formula will never know about its location or formatting. It receives a value or an array of values, and returns a value or am array of values.
Apps Script version
function workTime2() {
let file = SpreadsheetApp.getActive();
let sheet = file.getSheetByName("Sheet1");
let range = sheet.getRange('A1:A16');
let targetColor = "#00ffff"
let values = range.getValues(); // [[1],[2],[3]...]
let colors = range.getBackgrounds(); // [[#ffffff],[#00ffff],[#ffffff]...]
let sum = 0
for (let i = 1; i != values.length; i++){ // starting at 1 to skip first row
let value = values[i][0]
let color = colors[i][0]
if (color == targetColor) {
sum += value
}
}
let resultCell = sheet.getRange('B2');
resultCell.setValue(sum);
}
This script will sum the values in A1:A16 if they are turquoise. Putting the sum in B2.
This is a way to get a sum based of a cell value. This should give you a good starting point to customize to your liking.
Reference
getRange(a1Notation)
getValues()
getBackgrounds()
setValue(value)
In Google Sheets, I would like to change the cell value (in this case to add text) based on the cell's background color. For example, if the cell's background color is green (#62a84f) as in the picture attached, I would like to automatically insert text "g" in the cell.
Please see the picture
I found the following script but cannot figure out how to get it working:
function myFunction() {
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Prosinec+Leden");
var data1 = sheet1.getDataRange().getValues(); //2D array of sheet1 cell values
var bg;
for(var i=1; i<data1.length; i++)
{
bg = sheet1.getRange(?).getBackground();
if(bg == "#62a84f")
{
sheet1.getRange(?).setValue("x"); //Set value in corresponding row of sheet1
}
}
}
*Little background info - I work in an elementary school where we evaluate the progress of our pupils every week and it would save us a lot of work to have this script work.
Explanation:
While that's might not be an issue, the color "#62a84f" you selected is not one of the default colors in the color picker menu. In order to test the following code, I used the default #34a853:
But if you are sure that your color is indeed "#62a84f" then go ahead and use that and change the relevant part of my code to your case.
I used double map to iterate over every cell in the rng. The bigger the range the slower the performance of the code. I would highly advice you to use some specific range and not the whole data range.
Namely, you might want to replace:
const rng = sheet1.getDataRange(); with const rng = sheet1.getRange('B2:D10');
assuming that you want to check for green cells in the range 'B2:D10'.
Another reason you would want to use this approach is that you may have some green cells with no content outside of the DataRange and therefore your range won't contain these cells. So think carefully what is your scenario and choose the method that suits you the best.
Solution:
function myFunction() {
const sheet1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Prosinec+Leden");
const rng = sheet1.getDataRange();
const colors = rng.getBackgrounds();
const values = rng.getValues();
colors.map((r,i)=>r.map((c,j)=>{
if(c=='#34a853'){
values[i][j]="g";
}}));
rng.clearContent();
rng.setValues(values);
}
I am trying to copy the background color from a range of cells and paste it to a different range. In my case, my desired color is the range between rows 3 to 53 and every other column from E to BC. In A1 format, it would be (['E3:E53','G3:G53','I3:I53', ... 'BA3:BA53','BC3:BC53']). I want to get the background color of this range and then paste it to my target range, being between rows 3 to 53 and every other column from D to BB. This range in A1 notation would be (['D3:D53','F3:F53','H3:H53' ... 'AZ3:AZ53','BB3:BB53']).
In other words, I want every cell in my target range to be the same color as the adjacent cell to its right.
This is what I currently have.
This is my desired outcome using apps script.
I know I can change color manually, but the values I have in my sheet change frequently and the color of the cells I want to copy is based on conditional formatting rules, meaning I would have to manually change all the cell colors on a regular basis. That is why I want to use apps script, so all I have to do is run a function and it will create my desired effect for me.
I am very new to the world of coding, but this is what I have tried.
setColor(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Coach");
var targetArea = ss.getRangeList(['D3:D53','F3:F53','H3:H53','J3:J53','L3:L53','N3:N53','P3:P53','R3:R53','T3:T53','V3:V53','X3:X53','Z3:Z53',
'AB3:AB53','AD3:AD53','AF3:AF53','AH3:AH53','AJ3:AJ53','AL3:AL53','AN3:AN53','AP3:AP53','AR3:AR53','AT3:AT53','AV3:AV53','AX3:AX53','AZ3:AZ53',
'BB3:BB53']);
var desiredColor = ss.getRangeList(['E3:E53','G3:G53','I3:I53','K3:K53','M3:M53','O3:O53','Q3:Q53','S3:S53','U3:U53','W3:W53','Y3:Y53',
'AA3:AA53','AC3:AC53','AE3:AE53','AG3:AG53','AI3:AI53','AK3:AK53','AM3:AM53','AO3:AO53','AQ3:AQ53','AS3:AS53','AU3:AU53','AW3:AW53','AY3:AY53',
'BA3:BA53','BC3:BC53']);
var background = desiredColor.getBackgrounds;
targetArea.setBackgrounds(background)
}
Iv'e ran the code, but it tells me that "targetArea.setBackgrounds is not a function". If I remove the "s" from .setBackground, it does not do anything at all. No error or anything.
Any help would be greatly appreciated! Thank you!
The setBackground() method on the RangeList object allows you to only provide one color. Instead, you want to be calling setBackgrounds() (or setBackgroundObjects()) on the individual ranges. You can get those by calling getRanges() and then iterating through them to apply whatever changes you want.
function setColor() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Coach");
var targetRangeList = ss.getRangeList(['D3:D53','F3:F53','H3:H53','J3:J53','L3:L53','N3:N53','P3:P53','R3:R53','T3:T53','V3:V53','X3:X53','Z3:Z53',
'AB3:AB53','AD3:AD53','AF3:AF53','AH3:AH53','AJ3:AJ53','AL3:AL53','AN3:AN53','AP3:AP53','AR3:AR53','AT3:AT53','AV3:AV53','AX3:AX53','AZ3:AZ53',
'BB3:BB53']);
var sourceRangeList = ss.getRangeList(['E3:E53','G3:G53','I3:I53','K3:K53','M3:M53','O3:O53','Q3:Q53','S3:S53','U3:U53','W3:W53','Y3:Y53',
'AA3:AA53','AC3:AC53','AE3:AE53','AG3:AG53','AI3:AI53','AK3:AK53','AM3:AM53','AO3:AO53','AQ3:AQ53','AS3:AS53','AU3:AU53','AW3:AW53','AY3:AY53',
'BA3:BA53','BC3:BC53']);
var sourceRanges = sourceRangeList.getRanges();
var targetRanges = targetRangeList.getRanges();
for (var i = 0; i < sourceRanges.length; i++) {
targetRanges[i].setBackgrounds(sourceRanges[i].getBackgrounds());
}
}
By the way, you can probably get those ranges in a more programmatic way since the target ranges seems to be odd-numbered columns while the source ranges are even-numbered and they're all of the same height.
Issues:
The line var background = desiredColor.getBackgrounds; does not
actually execute getBackgrounds() because you forgot the parenthesis.
However, even if you didn't forget it, both getBackgrounds() and
setBackgrounds() are methods of a range object, not a
rangeList object as you use in your approach.
Solution:
Since the ranges are non-Contiguous you can't just use getRange(). Instead you can create a list of ranges and iterate over them to get the desired result.
function setColor(){
const ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Coach");
const targetArea = ['D3:D53','F3:F53','H3:H53','J3:J53','L3:L53','N3:N53','P3:P53','R3:R53','T3:T53','V3:V53','X3:X53','Z3:Z53',
'AB3:AB53','AD3:AD53','AF3:AF53','AH3:AH53','AJ3:AJ53','AL3:AL53','AN3:AN53','AP3:AP53','AR3:AR53','AT3:AT53','AV3:AV53','AX3:AX53','AZ3:AZ53',
'BB3:BB53'];
const desiredColor = ['E3:E53','G3:G53','I3:I53','K3:K53','M3:M53','O3:O53','Q3:Q53','S3:S53','U3:U53','W3:W53','Y3:Y53',
'AA3:AA53','AC3:AC53','AE3:AE53','AG3:AG53','AI3:AI53','AK3:AK53','AM3:AM53','AO3:AO53','AQ3:AQ53','AS3:AS53','AU3:AU53','AW3:AW53','AY3:AY53',
'BA3:BA53','BC3:BC53'];
const targetArea_Ranges = targetArea.map(ta=>ss.getRange(ta))
const desiredColor_Ranges = desiredColor.map(dr=>ss.getRange(dr))
targetArea_Ranges.forEach((tr,index)=>{
tr.setBackground(desiredColor_Ranges[index].getBackground())
});
}
The previous answers are right. You're calling methods that are not available on class Rangelist. In this answer, I would like to add a alternative approach, based on the fact that all your ranges are contiguous and follow a 1 to 1 pattern:
get all of the ranges in 1 swing: D3:BC53
getBackgrounds and copy paste colors in memory in array
setBackgrounds the modified array.
function setColors() {
const sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Coach');
const allArea = sh.getRange('D3:BC53');
const backgrounds = allArea.getBackgrounds();
for (const row of backgrounds) {
for (let i = 0; i < row.length - 1; row[i++] = row[i++]);
}
allArea.setBackgrounds(backgrounds);
}
I have a spreadsheet that may have any number of sheets on it at any given time. These "Side Sheets" have a total value added and placed in a specified cell. We'll say this total is in cell "A1" on every side sheet. I want to total all of these side sheet totals, and place the total in-cell on another sheet.
I've coded a solution I think should work, but it displays "loading" forever. I'm certain there's an easier way to do this, I just can't see it.
function GETSIDESHEETTOTALS(){
var totalCell = "A1"
var total = 0;
var cur_ss = SpreadsheetApp.getActive();
cur_ss.getSheets().forEach(function(sheet){
total += sheet.getRange(totalCell).getValue();
});
return total;
}
I'm expecting the totals from each sheet to add together and display in the cell I've specified on the main sheet. I've placed the function "=GETSIDESHEETTOTALS()" into a cell on the main page of my spreadsheet. I would prefer it to be a cell-called function if possible.
Does anyone have an alternate solution, or can tell me what I'm doing wrong?
For those familiar with Excel, this could be rephrased as, "How do I use Google App Script to sum using 3D cell references?".
Briefly looking at yours, you do not exclude the sheet on which you aggregate the total. Perhaps you're recursively adding the values together?
My very quick example from scratch:
function sum3D(cellRef) {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var cumTotal = 0;
for (var i=0;i<sheets.length;i++){
if (sheets[i].getIndex()!==1){ // specifically omit the first sheet
cumTotal += sheets[i].getRange(cellRef).getValue();
}
}
return cumTotal;
}
This is implemented in the first sheet in my Google Sheet as "=sum3d('A1')".
However, I would recommend designing this more generally to simply return an array upon which you can perform any function (average, multiplications, etc.).
E.g.
function get3D(cellRef) {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var arr = [];
for (var i=0;i<sheets.length;i++){
if (sheets[i].getIndex()!==1){
arr.push( sheets[i].getRange(cellRef).getValue());
}
}
return arr;
}
and implemented as, e.g., "=sum(get3d('A1'))".
EDIT
Some parts unnecessarily separated in the code have been consolidated (but the overall function remains the same)
EDIT 2
There are obvious improvements regarding how you designate the aggregator sheet. For example, you could simply pass in the sheet name in the formula and omit that based on the return value of "sheets[i].getName()".