How to apply the same conditional formatting rule to multiple columns in Google sheets using apps script? - google-apps-script

I have multiple columns - G, L, Q, V, AA, AF - to which I want to apply a conditional format rule in Google sheets. Starting in row 2 - so that I don't include the header row - I want any given cell in the specified column to .setBackgroundColor ("orange") if any text or data is found to the right of that cell. For example, I want cell G2 to be orange if cell H2 has anything entered inside of it, and L17 to be orange if S17 has data, and AA5 to be orange if AD5 is not blank.
My experience with apps script is very primative. I can only successfully write very few lines of code, so my dilemma is past my ability. I understand it is possible to apply conditional formatting using sheets' built in conditional formatting tab, but it will not work for my project seeing as I am gathering data with a Google form, and with every response I receive from the form, the sheet creates a new line for the submission that retains none of the formatting from the rest of the sheet. My plan would be to add a form submission trigger to the code so that the conditional formatting in the columns updates regularly with the sheet.
I have been looking around for some time online and have not found a solution to my problem. Any help would be greatly appreciated! Thank you!

The following example shows the process of creating a new conditional format rule, applying it to the sheet. Then copying and pasting the format of a range to a target range. You can find the references for the methods used in the references section at the end of this answer.
Example:
function myFunction() {
// The range where the formatting will be applied.
var range = 'G2:G'
// Get the array containing the conditional formatting rules for the sheet
var spreadsheet = SpreadsheetApp.getActive();
var conditionalFormatRules = spreadsheet.getActiveSheet().getConditionalFormatRules();
// Build the new conditional formatting rule and push it to the array
conditionalFormatRules.push(SpreadsheetApp.newConditionalFormatRule()
.setRanges([spreadsheet.getRange(range)]) // The range to apply to
.whenFormulaSatisfied('=H2<>""') // The formula to check
.setBackground('orange') // The format to apply
.build());
// Set the conditional format rules for the sheet with the updated array
spreadsheet.getActiveSheet().setConditionalFormatRules(conditionalFormatRules);
// Paste the format of the range into the next desired range
spreadsheet.getRange(range).copyTo(spreadsheet.getRange('L2:L'), SpreadsheetApp.CopyPasteType.PASTE_FORMAT, false);
/* repeat the previous instructions for all your target ranges replacing "L2:L" with the target range */
};
References:
-getConditionalFormatRules()
-newConditionalFormatRule()
-setConditionalFormatRules()
-copyTo(destination, copyPasteType, transposed)

Related

Google sheets Script include formula when adding new rows

I want to include a script on my sheet to include N/A with a formula to certain cells (Example: I6:K6 & N6) but only to include this if C6 contains "A or B" if C6 contains "C" then not to include the formula.
I wouldn't be able to use an array formula within a cell as the value where N/A would go may need to be manually overridden.
I have tried a few different ways and I can only get it to work to check if the current cell you are in contains the text and it will trigger to apply N/A in the other cells. However it will only do the N/A if you are still on the active cell, if you move off that cell it will stop the trigger.
if (values[i][0].match("A") != null) {
var nextCell = iis.offset(0, 6);
if (nextCell.getValue() === '');
nextCell.setValue("N/A");
}
}````
I have also tried another way but it will override all of the existing data I already have within my sheet which is not what I want.
````var lastRow = ii.getLastRow();
ii.getRange("I3").setFormula("=IF(REGEXMATCH(C3,\"A|B\"),\"N/A\",\"\")");
var fillDownRange = ii.getRange(3, 9, (lastRow - 1));
ii.getRange("I3").copyTo(fillDownRange);````
You could try an approach like I exposed in this answer, if you're able to add a couple of hidden columns (one before your current I and K, and other before N). This way you'll be able to use an ARRAYFORMULA they will expand to adjacent cells when you specify it. Let me know!

Trying to run macro to add formula onto the end of an entire google sheet column

I have recorded a macro that adds formula onto the end of a cell
function addpricemultiplier() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('H5:1000').activate();
spreadsheet.getCurrentCell().setFormula('=0.3*GOOGLEFINANCE("CURRENCY:USDAUD")');
spreadsheet.getRange('H5:1000').activate();
};
The problems I have are that it will only change if the value is =0.3, I am unsure how to change it so that it will add the formula on no matter what, and of less importance I have made the macro from H5 onto 1000, I am unsure if there is a way to make it just go on as long as the sheet is there instead of pre determining where it will go to.

Copy and paste, apply conditional formatting to multiple different ranges and different criteria

I am trying to apply conditional formatting to more than one range. The formatting is always the same but the cell that contains the condition is not.
For example:
`=OR($B$11="金",$B$11="木")` //this custom formula applies to range C5:Q14
'=OR($B$22="金",$B$22="木") //this custom formula applies to range C16:Q25
The first formula checks the cell B11 for condition
The second formula checks the cell B22 for the condition
It works as intended. However I have to set up many such ranges and if I copy or paste, the formatting ranges just get added to the ones already in the formula and they all check the same cell for the condition. I can achieve what I need if I set the condition for every range manually but I would like to know if there is a better way via sheets formula or a script if formulas are not viable.
Please see the sheet for the example
https://docs.google.com/spreadsheets/d/1G0eUibjNKlZ1fDm9id5SPFNlF392cuEPzNDMB8E5jyU/edit?usp=sharing
I see 3 possible abstractions of your problem.
1) Conditional formatting by groups of rows of known length. 2) How to identify the row index of the top of a section in a copy-paste friendly way if helper column is permitted. 3) Detect the top most non-blank cell in a column above a particular row.
Of course, 3) is most general. But if you only need 1) or 2), it's better to use simpler solutions. So I'll comment on each.
Conditional formatting by groups of rows of known length
Use a combination of index and match. (If the requirement grows more complicated, also consider using indirect.)
For example, if you need rows in groups of 11 to refer to the head row, you can do
=match(index($A:$A,floor(row(B1)/11)*11+1,1),{"金";"木"},0)
in B1:C11; given that you have weekday character in A:A every 11 rows.
Recall that in Conditional Formatting, we specify a (fixed) range and a formula that refers to relative ranges. Google Sheet will then iterate the cell indices over the (fixed) range -- meaning, starting with the top-left most cell, when you move down 1 row, the (relative) ranges in the formula will all have row index adds +1; when you move right 1 column, the (relative) ranges in the formula will all have column index adds +1. $ sign functions normally.
The only (relative) range in the formula that is free to iterate is B1. Thus what happens here is that: as you move along (fixed) range B1:C11, for example when you reach C10, the (relative) range in the formula becomes C10 (coincidentally) because C10 is 9 rows down and 1 column right from the top-left most cell in range B1:C11.
Test:
Input
Result
Identify/Designate row index of the top of a section
If you can use a helper column, there is an easy way to designate a row index as a function of the position of a section.
For example, let's say your section spans B1:D10. You want to be able to copy-paste this section to B21:D21 and you want everything else to keep.
It's simple if you can tolerate using A:A just for labeling the top of the section.
You do not need to know the number of rows per section ahead of time.
In A1, input =row(A1). In A2, input =A1. Now drag the formula across your section, ie. to A10.
Now you can put conditional formatting in C1:D10 as simply
=match(index($B:$B,A1,1),{"金";"木"},0)
and of course you can use simpler formulas for string comparison.
Detect the top most non-blank cell in a column above a particular row
If you don't have a fixed template for your section with known number of rows, and you need to keep your sheet clean of helper columns, then the only way is to detect the last non-empty cell above a given row in the column you have your weekday characters.
A number of variations are possible here. You can detect non-empty cell. Or you can detect the presence of a string from a list of string. You can retrieve the content of the cell or you can get the row index. etc. We are going to do the simplest thing here.
Suppose you have your weekday characters in A:A and you need conditional formatting in B:C. Then, in Conditional Formatting tab, put range as B:C, and formula as follows.
=match(+SORT($A$1:$A1,$A$1:$A1<>"",,ROW($A$1:$A1),),{"金";"木"},0)
What happens here is that +SORT($A$1:$A1,$A$1:$A1<>"",,ROW($A$1:$A1),) will pick out the content of the last non-empty cell in column A relative to the row in question.
Here is how SORT achieves the result for us:
The 2nd input of SORT will evaluate into a column of boolean with TRUE meaning non-empty. $A$1:$A1 relative to B1:C means to pick out cells in A1:A above and including the cell in question. Leaving the 3rd input empty means descending, which in turn means TRUE comes before FALSE. + tells Google Sheet to output the first element in an array output. Up to this point, +SORT($A$1:$A1,$A$1:$A1<>"",) will output the top-most non-empty cell. Within all the non-empty cells, you want the last one. Hence, the 4th input for row index and 5th input for descending. The non-empty cell with the highest row index in A1:A is the cell you want.
It is up to you as the Asker to identify the exact requirements for your task at hand.
I would say it is important that the Asker abstracts the requirements and state them in the question --- as opposed to seeking how to assess the task at hand in answers.
That is what often distinguishes a programming question that everyone else can search easily, thus learn and adapt from vs an outsourcing query.
Apps Script Solution
AFAIK the best way is with Apps Script
With a script like this:
function createRule() {
// Get Ranges
var sheet = SpreadsheetApp.getActiveSheet();
var cellToWatch = sheet.getActiveCell()
var rangeForRule = cellToWatch.offset(-6, 1, 10, 15)
// Create absolute Cell reference
var cellNotation = cellToWatch.getA1Notation()
var patt = /([a-zA-Z]+)([\d]+)/
var result = patt.exec(cellNotation)
var absoluteRef = "$" + result[1] + "$" + result[2]
// Create Conditional Formatting rule
var rule = SpreadsheetApp.newConditionalFormatRule()
.whenFormulaSatisfied('=OR('+ absoluteRef +'="金",'+ absoluteRef +'="木")')
.setBackground("#00FF00")
.setRanges([rangeForRule, cellToWatch])
.build();
var rules = sheet.getConditionalFormatRules();
rules.push(rule);
sheet.setConditionalFormatRules(rules);
}
This script
Assumes that your sheet will always have exactly the same format
Needs you to select the cell with the criteria, like this:
At the moment you need to run from the script editor
It chooses a range to apply the formatting rule based on the position of the selected cell. var rangeForRule = cellToWatch.offset(-6, 1, 10, 15)
It gets the A1 notation of the selected cell and using RegEx, makes an absolute version of the A1 notation. A1 => $A$1
It creates a conditional formatting rule using the references it has just built.
You will need to modify the HEX value of the color to suit your needs "#00FF00"
You could make this a custom sidebar on your spreadsheet, so that creating this rule is just a couple mouse clicks.
References
Apps Script Main Page
RegEx
Sheets Range Object
Conditional Format Rule Builder
Conditional Format Rule Class

How to remove data validations?

I want to remove all data validations from a sheet, tried following but it does not get rid of them. Anyway to do this through apps script?
var ss = SpreadsheetApp.getActiveSpreadsheet();
var accountSheet = ss.getSheetByName("Account A");
accountSheet.getRange(1,1,274,61).clear();
accountSheet.getRange(1,1,274,61).setDataValidation(null)
That should do the job.
A more generic answer to remove all data validations from the whole sheet (remove from all cells in sheet):
mySheet.getRange(1,1, mySheet.getMaxRows(), mySheet.getMaxColumns()).setDataValidation(null);
You could in theory use this less verbose version (might be faster because it affects a smaller range of cells):
mySheet.getDataRange().setDataValidation(null);
However, there may be empty cells outside the DataRange that do have validations and those validations will not be removed (they are outside the scope of DataRange, won't be selected by DataRange). Hence, you should use the first version I mentioned for more robust code: it selects all the cells in the sheet and removes validations.

Google Spreadsheet - Insert cut/copied cells/rows

Google Spreadsheet doesn't have the functionality to "insert cut cells" like in Excel.
Let's say I selected A4 to B5, hit Ctrl+X.
Then I moved the selection to A2.
Now I want to "insert cut cells", probably by inserting blank cells and moving the dotted-range to A2.
Is there any way to do it using JavaScript on your own menu?
eg.
function insertCutOrCopiedCells(){
var SS = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SS.getActiveSheet();
var cell = sheet.getActiveCell();
// How do you get the cells being cut/copied? (ie. A4 to B5)
// We can then insert empty cells (2 cols 2 rows) at the selection (A2)
// and move the cut cells (A4 to B5) there
}
To achieve the insert cut cells feature in google sheets you simply use drag and drop. First highlight the row(s) you want to move then mouse over the row number (you will see a hand icon); then, holding your left-click, and keeping your mouse over the row numbers, you will see a dark line where the insert will take place. Let go of your mouse button and voila.
You have both methods, check'em in the class Range.
moveTo(target)
Cut and paste (both format and values) from this range to the target range.
copyTo(destination)
Copies the data from a range of cells to another range of cells. Both the values and formatting are copied.
Edit:
To complete the function you'll have to use also:
insertRowsAfter(afterPosition, howMany) in class Spreadsheet
Inserts a number of rows after the given row position.
and
getActiveRange() with getRow() to check where's the selection at:
Returns the range of cells that is currently considered active. This generally means the range that a user has selected in the active sheet, but in a custom function it refers to the cell being actively recalculated.
Since you don't have direct acess to the clipboard, you'll have to set up a sidebar, or a modelessDialog, which asks for a range to copy from, and it would paste into the selected area, or the other way around, paste the current selected area onto an inputed ROW.
I think , you can separate the function.
Copy : use getRange with getValue:
link => https://developers.google.com/apps-script/reference/spreadsheet/sheet
Delete data : use the getRange with setValue = blank
example :
var Spreadsheet=
SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName("number1");
var value = Spreadsheet.getRange(1,0).getValue(); // copy
Spreadsheet.getRange(1,1).setValues(value); // insert
Spreadsheet.getRange(1,0).setValues("");
You can use the metho copyTo
example :
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange("A:E").copyTo(sheet.getRange("F1"), {contentsOnly:true});
}