use ISFORMULA with ARRAYFORMULA on a range of cells - arrays

I have a row of cells, some with a formula, and some without.
I am trying to use ARRAYFORMULA WITH IFFORMULA to see if a cell has a formula or not. This is the function I am using:
=ARRAYFORMULA(ISFORMULA(B2:B))
But it just outputs one single value.
There are other things I need to do for each row/cell which is why I need ARRAYFORMULA.
Is there another way to get ISFORMULA to work with ARRAYFORMULA?

not all functions are convertible into ArrayFormula. ISFORMULA is one of those...
the best you can do is pre-program it like:
={ISFORMULA(B2);
ISFORMULA(B3);
ISFORMULA(B4);
ISFORMULA(B5);
ISFORMULA(B6)}
the next best thing you can do is to use conditional formatting to color it like:
green color:
=ISFORMULA($B2)
red color:
=NOT(ISFORMULA($B2))*(B2<>"")
UPDATE
now possible with lambda:
=BYROW(B2:B6; LAMBDA(x; ISFORMULA(x)))

This is so frustrating. I myself have made areFormula() by my needs. Not so elegant, but working. It takes a range of row or column (if area, treated as a row), and returns a row.
function areFormula(rowOrColumn) {
// takes a range as input parameter
// from https://webapps.stackexchange.com/a/92156
var sheet = SpreadsheetApp.getActiveSheet();
var formula = SpreadsheetApp.getActiveRange().getFormula();
var args = formula.match(/=\w+\((.*)\)/i)[1].split('!');
try {
if (args.length == 1) {
var range = sheet.getRange(args[0]);
}
else {
sheet = ss.getSheetByName(args[0].replace(/'/g, ''));
range = sheet.getRange(args[1]);
}
}
catch(e) {
throw new Error(args.join('!') + ' is not a valid range');
}
rowOrColumn = range;
//here starts my code
let values = rowOrColumn.getValues();
let isItRow = true;
if(values.length == 1) {
isItRow = false;
values = values[0];
}
else {
values = values.map(el => el[0]);
}
let result = [];
let x = 0, y = 0;
for(let i = 0; i < values.length; i++) {
const cell = rowOrColumn.getCell(1+y, 1+x);
result.push(cell.getFormula() ? true : false);
if(isItRow)
y++;
else
x++;
}
console.log(result);
return(result);
}

Related

Searching for a value and if a value exists adding a row with copying the data in google spreadsheet

I want to search for a word in a certain column and then if the value exists, I want to copy the row below with its values and change the word to two different words.
My issue was in getting the found word row Number to insert a row below it.
function myFunction() {
var ss = SpreadsheetApp.openById("1fE404JUbw3aytlqtoht6FfrIhYhTpSe2MM5UDnBkFc4");
var sheet = ss.getSheets()[0]
let range = sheet.getDataRange();
var values = range.getValues();
var typeRange = sheet.getRange("E2:E");
var typeValues = typeRange.getValues();
var i;
for(i = 0; i <= lastRow ; i++){
for (let type of typeValues){
if (type == "Both"){
var bothRow = i+1;
}
//// var bothRow = sheet.getRange(i+1,1,1,typeValues[i].length);
//// ss.insertRowsAfter(bothRow, 1);
}
}
}
I have used alert to check and it inserted an infinite number of rows after row number 1.
function myFunction() {
let sheetui= SpreadsheetApp.getUi()
sheetui.createMenu("Rahaf Game")
.addItem("Stage 1", 'pop')
.addToUi();
// var ss = SpreadsheetApp.openById("1fE404JUbw3aytlqtoht6FfrIhYhTpSe2MM5UDnBkFc4");
// var sheet = ss.getSheets()[0]
// let range = sheet.getDataRange();
// var values = range.getValues();
// var typeRange = sheet.getRange("E2:E");
// var typeValues = typeRange.getValues();
}
function pop(){
//var ss = SpreadsheetApp.openById("1fE404JUbw3aytlqtoht6FfrIhYhTpSe2MM5UDnBkFc4");
var sheet = ss.getSheets()[0]
var typeRange = sheet.getRange("E2:E");
var typeValues = typeRange.getValues();
var lastRow = sheet.getLastRow();
var i;
for(i = 0; i <= lastRow ; i++){
for (let type of typeValues){
if (type == "Both"){
var bothRow = i+1;
ss.insertRowsAfter(bothRow, 1);
}
//// var bothRow = sheet.getRange(i+1,1,1,typeValues[i].length);
//// ss.insertRowsAfter(bothRow, 1);
}
}
}
Can someone please help in achieving the required result in inserting a row below and copy the values into it with changing the word into two words?
Answer:
.getValues() returns a 2D array of values and you are referencing it as if it is unidimentional.
Code fix:
You need to change your conditional such that the object you are checking is an array:
if (type == "Both"){
//code
}
Should be:
if (type == ["Both"]){
//code
}
Adding a row and copying the data to it:
You can add a row in a sheet after a given row with the insertRowsAfter() and use .getRange().setValues() to copy in the data:
for(var i = 0; i <= lastRow ; i++){
for (let type of typeValues){
if (type == ["Both"]){
var bothRow = i + 1;
ss.insertRowsAfter(bothRow, 1);
// increment lastRow as there is now one more row in the sheet
lastRow++;
// increment i as you want to skip the row you just copied!
i++;
var rangeToCopy = sheet.getRange(bothRow + ':' + bothRow).getValues();
sheet.getRange((bothRow + 1) + ':' + (bothRow + 1)).setValues(rangeToCopy);
}
}
}
Don't forget to increment lastRow by 1 if you add a new row so that your loop still reaches the end of the sheet.
References:
Class Range | Apps Script - Method getValues()
Related Questions:
Using Google Sheets scripts, why does my if statement always return false when comparing cell values?

Is there a way to exclude cells that contain formulas from my Google Script?

Apologies is this has been asked already but if someone could point me in the right direction that would be great. I've looked for several versions/solutions to this but can't seem to find one (I may be looking in the wrong place).
Basically, I'm looking for a way to exclude any cells that contain a formula from the following script. So I only want it to run this on cells that exactmatch "1". Any help would be greatly appreciated.
function runReplaceInSheet(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("2020");
// get the current data range values as an array
var values = sheet.getDataRange().getValues();
// Replace 1 with HOL
replaceInSheet(values, "1", "HOL");
// Write all updated values to the sheet, at once
sheet.getDataRange().setValues(values);
}
function replaceInSheet(values, to_replace, replace_with) {
//loop over the rows in the array
for(var row in values){
//use Array.map to execute a replace call on each of the cells in the row.
var replaced_values = values[row].map(function(original_value) {
return original_value.toString().replace(to_replace,replace_with);
});
//replace the original row values with the replaced values
values[row] = replaced_values;
}
}
Flow:
getFormulas and replace only if the array of formulas is empty
Sample script:
function replace(sheetName = '2020', searchValue = '1', replaceValue = 'HOL') {
const sheet = SpreadsheetApp.getActive().getSheetByName(sheetName);
const range = sheet.getDataRange();
const formulas = range.getFormulas();
const values = range.getValues();
range.setValues(
formulas.map((row, i) =>
row.map(
(formula, j) =>
formula || values[i][j].toString().replace(searchValue, replaceValue)
)
)
);
}
You may use the getFormulas() method to get all the formulas in the current shet and iterate through them like a 2d array.
var values = sheet.getDataRange().getDisplayValues();
var formulas = sheet.getDataRange().getFormulas();
for (var r=0; r<values.length; r++) {
for (var c=0; c<values[r].length; c++) {
var cellValue = values[r][c];
var formula = formulas[r][c];
if (!formula && cellValue === "1") {
sheet.getRange(r+1, c+1).setValue("HOL")
}
}
}

Dynamic if statement in Google script

I have the function below which sets values in an array to "" if each value in the row is identical and then removes the blank values from the array
If I have
arr = [[[a],[a]],[[a],[b]],[[a],[c]],[[b],[a]],[[b],[b]],[[b],[c]],[[c],[a]],[[c],[b]],[[c],[c]]]
I end up with
[[[[a],[b]],[[a],[c]],[[b],[a]],[[b],[c]],[[c],[a]],[[c],[b]]]]
But what if my array has n dimensions instead of just two
I can't figure out how to write this part dynamically
for (var i = 0; i < values.length; i++) {
if (values[i][0] == values[i][1]) {
values[i][0] = '';
values[i][1] = '';
}
}
So that the if statement was testing
values[i][0] == values[i][1]... ==values[i][n]
Thanks
My Function
function removeEmptyRng(shtName, rng, outputRng) {
var sht = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(shtName);
var lr = sht.getLastRow();
var range = sht.getRange(rng + lr);
var values = range.getValues();
for (var i = 0; i < values.length; i++) {
if (values[i][0] == values[i][1]) {
values[i][0] = '';
values[i][1] = '';
}
}
//Remove Empty values
values = values.filter(function(a) {return a.filter(Boolean).length > 0;});
if (outputRng == true)
{sht.getRange(1,2,values.length,values[0].length).setValues(values)};
return values
}
You could immediately filter for the inequality in the whole row... Basically saying "keep a row if one of its values is unequal to the first of the row" => not all are equal.
values = values.filter(
function(row) {
return row.some(
function(val) {
return val != row[0]});
});

Reset checkboxes to false

I have a Google spreadsheet where column A has checkboxes in each row. I have written a script to perform a function on all rows where the checkboxes are checked, but I want to add in at the end a reset function so that all checked boxes are unchecked again after the script is run.
I've tried using a for loop like this:
var dataRange = sheet.getRange('A3:A');
var values = dataRange.getValues();
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[i].length; j++) {
if (values[i][j] == true) {
values[i].setValue(false);
}
}
}
But clearly this doesn't work as I get an error.
Does anyone know how this could be done?
How about this modification? I think that there are several solutions for your situation. So please think of this as one of them.
Modification points :
The reason of the issue is values[i].setValue(false);. values[i] is an array. Please use the range for setValue().
But to use setValue() in the for loop leads to higher cost. So in this modification, I used setValues().
Put "false" to values, if values[i][j] is "true".
Put the modified values to the sheet using setValues().
Modified script :
var dataRange = sheet.getRange('A3:A');
var values = dataRange.getValues();
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[i].length; j++) {
if (values[i][j] == true) {
values[i][j] = false; // Modified
}
}
}
dataRange.setValues(values); // Added
Reference :
setValue()
setValues()
If this was not what you want, please tell me. I would like to modify it.
Update:
You can now use range.uncheck() directly on the range
Alternatively, Since you want to uncheck everything in the range (and all of the range have checkboxes in them), just do:
sheet.getRange('A3:A').setValue(false);
without checking/looping.
By highlighting all the cells in the range that you want to "uncheck" hold Crtl and double tap the spacebar.
The remove-check-marks method is "uncheck"…
var range = SpreadsheetApp.getActive().getRange('A21:A59');
range.uncheck();
This solution posted above does reset all checkboxes;
however, it also unexpectedly changes cells with the value of 1 in it to FALSE.
I want to make a script to reset checkboxes on ALL sheets...
function resetCheckBoxesAllSheets() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
for (var s in allsheets){
var sheet=allsheets[s]
var dataRange = sheet.getRange('A4:Z100');
var values = dataRange.getValues();
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[i].length; j++) {
if (values[i][j] == true) {
values[i][j] = false; // Modified
}
}
}
dataRange.setValues(values);
}//end of sheets loop.
}// end of function...
I have set one checkbox instead of caption as shown in the picture
I write a script for check the data range and check uncheck all accordingly.
Here is the script.
function onEdit(e){
Logger.log('OnEdit');
var range = e.range;
var cl = range.getColumn();
var rw = range.getRow();
Logger.log('Row = ' + rw + ' Column = ' + cl);
var ActiveSheet = ss.getActiveSheet();
if(ActiveSheet.getName() === 'Companies' && rw == 1 && cl == 5){
var isAllTrue = ActiveSheet.getRange(rw, cl).getValue();
if(isAllTrue == true){
var Avals = ActiveSheet.getRange("A1:A").getValues();
var Alast = Avals.filter(String).length;
ActiveSheet.getRange(2, 5, Number(Alast-1)).setValue(true);
}
else
{
var Avals = ActiveSheet.getRange("A1:A").getValues();
var Alast = Avals.filter(String).length;
ActiveSheet.getRange(2, 5, Number(Alast-1)).setValue(false);
}
}
}
Change sheet name as per your need.
Note: create onedit trigger for same function if not trigger auto.

Cant make the condition formatting to work with a script in Google sheets

I am trying to color format a row in a spreadsheet where I run a loop to check if any of the values is equal to 0 and if it is equal to 0 to color it red and if not color it red.
I know I can do it with conditional format but I need this to activate after I press a button included in the sheet, so I need a script added to the button.
I have managed to write this script but it seems it color only the last cell from the range.
Can you please tell me what am I doing wrong since I am new to coding.
function FormatColor() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Acontobetaling');
var range = sheet.getRange('D34:AA34');
var values = [];
var cell = [];
for (var i = 1; i <= 24; i=i+1)
cell = range.getCell(1, i)
values = cell.getValue();
if (values = '0') {
cell.setBackground('red');
} else {
cell.setBackground('green');
}
}
I made it work, if someone is having the same problem you can use this
Anyway if you can provide me with better or fast way to do it you are welcome to show me how :)
function CopyData() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Acontobetaling');
var rangeColor = sheet.getRange('D34:AA34');
var values = [];
var cell = [];
var colors = [];
for (var i = 1; i <= 24; i=i+1) {
cell = rangeColor.getCell(1, i);
values = cell.getValue();
if (values == '0') {
colors = cell.setBackground('red');
} else {
colors = cell.setBackground('green');
}
}

Resources