Google Apps Script getBackgrounds for colour custom function - google-apps-script

How do you write a custom function to get a cell's colour, or an array for a range's colour?
There's an example given by Google for how to optimise your functions when they use ranges, but how do you get data about the range rather than do maths on it?
Example given:
function DOUBLE(input) {
return Array.isArray(input) ?
input.map(row => row.map(cell => cell * 2)) :
input * 2;
}
Basic idea:
function COLOUR(input) {
return Array.isArray(input) ?
input.map(row => row.map(cell => cell.getBackground())) :
input.getBackground();
}
or maybe like this?
function COLOUR(input) {
return Array.isArray(input) ?
input.map(row => row.map(cell => getBackground(cell))) :
getBackground(input);
}
This code doesn't actually work, the first gives a type error and says you can't read getBackground of null, and the second one says getBackground is not defined, but hopefully you get what I'm trying to do. Help?

Issue:
getBackground() is a method applied to a range object. In your code, cell is a value.
Solution:
To get the background colors of a range, you need to get the range object of the particular cell references and then apply getBackgrounds to get the hex color codes
of your input.
You just need to pass the input as a string though e.g. =COLOUR("A1:B2").
function COLOUR(input) {
const ss = SpreadsheetApp.getActive();
const sh = ss.getActiveSheet();
const bcolors = sh.getRange(input).getBackgrounds();
return bcolors;
}
Example output:
If you want to get the color names, then you need to map the hex color codes (in bcolors) to actual color names and return the names instead.

Related

GAS : How to format all empty cells in a whole sheet

I'd like to format all empty/blank cells in a whole sheet with a white background but I don't find the best method to retrieve a range that can handle all those empty/blank cells. I don't want to create a conditional rule, I'd like to handle it within a script.
What kind of method should I use ? Thanks a lot !
All empty/blank cells alas, as far as I know, there is no such range in SpreadsheetApp. You have to specify some max area. Say, "A1:ZZ". In this case the solution is quite trivial:
function set_all_backgrounds() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange('A1:ZZ');
var backgrounds = range.getValues().map(row =>
row.map(cell => { if (cell === '') return "white" } ));
range.setBackgrounds(backgrounds);
}
Of course if you will try to use column 'AAA' it could not have the white background.
If you mean all empty/blank cell inside data range, you can get the range this way:
var range = sheet.getDataRange();

Google Sheets - Add to Certain Value Based on Background Color

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)

Bug in getBackgrounds() using custom theme colors

I've got a spreadsheet with colors from a custom theme.
When I read the colors using getBackgrounds() the colors returned are all #000000
If I read the colors cell-by-cell using getBackground() the colors are returned correctly.
If I use standard colors (ie not colors in my Theme) the colors are also returned correctly.
TEST SHEET
(available to view at https://docs.google.com/spreadsheets/d/1nCZeUbCjs_5p6_52v8ggqVgrgnJ-Pd6x-gzXUFfV8G0/edit?usp=sharing
Cells A1:D1 contain the names of the four Beatles, all with background color #b70906
TEST CODE
/** #OnlyCurrentDoc */
function getbackgroundstwoways(){
var fullrange= SpreadsheetApp.getActiveSpreadsheet().getRange("A1:D1");
// Read all cells using getBackgrounds
var arBack = fullrange.getBackgrounds();
var arValues=fullrange.getValues()
Logger.log("Full array " +arBack + arValues);
//Now do the cells individually with getBackground
for (var i=0; i<fullrange.getLastRow();i++){
for (var j=0; j<fullrange.getLastColumn();j++){
Logger.log("Single cell " + i + " " + j + " " + fullrange.offset(i,j).getBackground() + " " + fullrange.offset(i,j).getValue() ) ;
}}}
LOGGER OUTPUT
Full array #000000,#000000,#000000,#000000John,Paul,George,Ringo
Single cell 0 0 #b70906 John
Single cell 0 1 #b70906 Paul
Single cell 0 2 #b70906 George
Single cell 0 3 #b70906 Ringo
How about this answer?
Issue and solution:
When I saw your shared Spreadsheet, the background colors of cells "A1:D1" has the color type of "THEME". I think that this is the reason of your issue.
In the current stage, it seems that getBackground() can directly retrieve the background color from the color type of "THEME" as the hex string. But, it seems that getBackgrounds() cannot directly retrieve them. The retrieved values using it becomes #000000. I'm not sure whether this is the bug or the current specification. But in the current stage, the background colors of the color type of "THEME" can be retrieved by the methods in Spreadsheet service.
One of several solution is to use getBackground() as your script. This has already been achieved in your script.
In this answer, as another pattern, the colors are retrieved from the values retrieved by getThemeColors. The flow of this script is as follows.
Create an object for searching the colors from the theme color type.
Retrieve background objects.
Retrieve the background colors from backgroundObjects.
Sample script:
function getbackgroundstwoways() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const fullrange = ss.getActiveSheet().getRange("A1:D1"); // Range from your script.
// 1. Create an object for searching the colors from the theme color type.
const theme = ss.getSpreadsheetTheme();
const themeColorObj = theme.getThemeColors().reduce((o, e) => Object.assign(o, {[e]: theme.getConcreteColor(SpreadsheetApp.ThemeColorType[e]).asRgbColor().asHexString()}), {});
// const fullrange = SpreadsheetApp.getActiveSpreadsheet().getDataRange();
// 2. Retrieve background objects.
const backgroundObjects = fullrange.getBackgroundObjects();
// 3. Retrieve the background colors from backgroundObjects.
const backgroundColors = backgroundObjects.map(r => {
return r.map(c => {
if (c.getColorType() == SpreadsheetApp.ColorType.RGB) {
return c.asRgbColor().asHexString();
} else if (c.getColorType() == SpreadsheetApp.ColorType.THEME) {
return themeColorObj[c.asThemeColor().getThemeColorType()];
} else {
return null;
}
});
});
console.log(backgroundColors);
}
This script can be used for the color types of both "RGB" and "THEME". When this script is run at your shared Spreadsheet, [ [ '#b70906', '#b70906', '#b70906', '#b70906' ] ] can be seen at the log.
Even when the RGB types and the THEME types are mixed, this script can retrieve the background colors as the hex string.
Note:
In this case, the background colors are retrieved from the theme colors. But when you overcoat the background colors as the RGB type of #b70906, you can retrieve them using getBackgrounds().
When I searched about this that getBackgrounds() cannot be used for the THEME color type at the issue tracker, I couldn't find this. So how about reporting this? Ref
Please use this script with V8.
References:
getSpreadsheetTheme()
This method is added at December 18, 2019.
Class SpreadsheetTheme
getBackgroundObjects()

Copying background color from one column to another with a custom function

I wrote this seemingly simple script to copy the background colors from one column to another.
When I run the script, I get no error messages, but nothing seems to happen.
This is what I type into a cell on the sheet:
=copyColor("B:B", "A:A")
I read a post on the Google Apps Script forum that implied that this type of procedure isn't possible, but I am determined to write a script that pulls it off.
Here is what the post said:
"As it is clearly explained in the documentation, Custom functions return values, but they cannot set values outside the cells they are in. In most circumstances, a custom function in cell A1 cannot modify cell A5. That is of course also true for other methods such as setBackground etc."
This is why I tried to get around the problem, by NOT USING the setBackgrounds() function.
Is there another way? Or is there a way I can fix mine to make it work?
function copyColor(rangeToCopy, rangeToPaste)
{
//an array to store the first background colors
var firstColors = [];
//an array to store the second background colors
var secondColors = [];
//this will assign the first range into a variable
var firstRange = SpreadsheetApp.getActiveSheet().getRange(rangeToCopy);
//this will store the colors of the range into the firstColors array
firstColors = firstRange.getBackgrounds();
//this will assign the second range to a variable
var secondRange = SpreadsheetApp.getActiveSheet().getRange(rangeToPaste);
//this will store the colors of the range into the secondColors array
secondColors = secondRange.getBackgrounds();
//compare the two color arrays. if they do not match, apply the first array to the second array
if (firstColors != secondColors)
{
secondColors = firstColors
}
}

How to set the background color of the return value of a Google Sheets custom function

I was wondering is someone can help me figure out how to do the following:
I have a custom function that returns a number and under a
specific condition, let say number equals 1, I want the function to
return the number and color the cell background as well.
I have to check the condition within the function and not do a conditional
formatting from the outside.
any suggestions?
You should take a look at Range classe, on setBackground(string) or on setBackgroundRGB(int, int, int) method, which give you the ability to color your range, as you wish.
Edit: Here is a workaround for using setBackground method in your case:
function onEdit(e) {
var result = e.range.getValue();
// Test your condition to change the color
if(result > 3){
var cell = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getActiveCell();
cell.setBackground("red");
}
}
The function will put a red background for all modified value who will be more than 3.