Find all empty cells in several columns - google-apps-script

Is there an easy way to color the rows containing empty cells in specific columns? So far, I only came up with a solution to highlight the cells themselves, but this script also takes some time to run.
Will appreciate any advice or guidance!
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('FRs & NFRs')
var columns = ['J17:J','L17:L', 'V17:V', 'AB17:AB','AI17:AI', 'AK17:AK'];
for(var col in columns){ //loop to iterate the desired ranges
var range = sheet.getRange(columns[col]);
//range.activate();
var values = range.getValues();
var formulas = range.getFormulas();
//for each row that data is present
for(var i = 0; i < values.length; i++) { //loop to iterate the rows
for( var j = 0; j< values[i].length ; j++){ //loop to iterate the columns
var cell = range.getCell(i+1, j+1);
if ( values[i][j] == "" )
{
cell.setBackground('red');
cell.activate
}
else
{ cell.setBackground('white')
}

Use ConditionalFormatRuleBuilder instead. This will create a Conditional format rule in your Sheet.
Try this.
Code:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('FRs & NFRs');
var ranges = sheet.getRangeList(['J17:J','L17:L', 'V17:V', 'AB17:AB','AI17:AI', 'AK17:AK']);
var rule = SpreadsheetApp.newConditionalFormatRule()
.whenCellEmpty()
.setBackground('red')
.setRanges(ranges.getRanges())
.build();
var sheetRules = sheet.getConditionalFormatRules();
sheetRules.push(rule);
sheet.setConditionalFormatRules(sheetRules);
}
Output:
Conditional Formatting:
Highlighting empty cells using rangeList.setBackgroundColor(color) ​
In my code below, I collected the cell name of the empty cells and use it to create a RangeList. Then I used the RangeList to set the background to red. This reduced the Google Sheet Service calls from thousands to 11.
function columnToLetter(column)
{
var temp, letter = '';
while (column > 0)
{
temp = (column - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
column = (column - temp - 1) / 26;
}
return letter;
}
function highlightEmptyCell(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('FRs & NFRs')
var rangeList = sheet.getRangeList(['J17:J','L17:L', 'V17:V', 'AB17:AB','AI17:AI', 'AK17:AK']);
var rangeArray = rangeList.getRanges();
var ranges = [];
rangeArray.forEach(range =>{
var data = range.getValues();
for( var i = 0; i < data.length; i++ ){
for( var j = 0; j < data[0].length; j++ ){
if(data[i][j] == ""){
var cellName = columnToLetter(range.getColumn()) + (i + 17)
ranges.push(cellName);
}
}
}
});
sheet.getRangeList(ranges).setBackground("red");
}
Reference:
Class ConditionalFormatRuleBuilder
RangeList.setBackgroundColor(color)

Related

Google sheets Count cells by color if value in cell is equal to 1 or more

I'm working in Google sheets. I'm not a developer but I got this function from a YouTube video. In my Google sheet I have a list of task in column A and these tasks award 0-5 points when complete. The function I have looks at the range specified counts how many of them are green. works fine but I only interested in counting how many tasks are complete that award points of 1 or more. Here is the function:
function countColoredCells(countRange,colorRef,rangeSum) {
var activeRange = SpreadsheetApp.getActiveRange();
var activeSheet = activeRange.getSheet();
var range = activeSheet.getRange(countRange);
var rangeHeight = range.getHeight();
var rangeWidth = range.getWidth();
var bg = range.getBackgrounds();
var colorCell = activeSheet.getRange(colorRef);
var color = colorCell.getBackground();
var count = 0;
for(var i = 0; i < rangeHeight; i++)
for(var j = 0; j < rangeWidth; j++)
if( bg[i][j] == color )
count = count+1;
return count;
};
To explain this code a bit more it takes 3 augments. The First points to a that has a range in it. In my sheet in cell C174 I have written there C2:C173. So I give the function C174 for the first augment and it looks there for the range. The second augment takes a cell with the color you want to be counted. So I have the G5 cell Colored Green so I give the functions second augment G5 because I want it to count the green cells. The third augment is a little tricky to explain but you give the function a cell that as a Boolean check box thing and when you change it's value it refreshes the function to update it's count. Here is my google sheets https://docs.google.com/spreadsheets/d/1W05-UKB-bBGMqOUC5OO9XirxnRBfJeMOj3ZMHoENcSQ/edit?usp=sharing There is a second custom function I am using here that I got from the YouTube video that just looks at the range of colored and sums their value. Also if you are looking at this sheet and are confused. the values in the colored cells that are formatted to be hidden.
You can try to make these three changes in the function:
function countColoredCells(countRange,colorRef,rangeSum) {
var activeRange = SpreadsheetApp.getActiveRange();
var activeSheet = activeRange.getSheet();
var range = activeSheet.getRange(countRange);
var rangeHeight = range.getHeight();
var rangeWidth = range.getWidth();
var bg = range.getBackgrounds();
var colorCell = activeSheet.getRange(colorRef);
var color = colorCell.getBackground();
var points_range = countRange.replace(/[A-z]+/g,'B'); // <--- HERE
var points = activeSheet.getRange(points_range).getValues(); // <--- HERE
var count = 0;
for(var i = 0; i < rangeHeight; i++)
for(var j = 0; j < rangeWidth; j++)
if( bg[i][j] == color && points[i][0] > 0 ) // <--- HERE
count = count+1;
return count;
};
Count Colors
function colorCounter() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");//source sheet
const cs = sh.getDataRange().getBackgrounds();
const vs = sh.getDataRange().getValues();
let obj = { pA: [] };
//color counter
vs.forEach((r, i) => {
r.forEach((c, j) => {
if (!isNaN(c) && c >= 1) {
if (!obj.hasOwnProperty(cs[i][j])) {
obj[cs[i][j]] = { color: cs[i][j], count: 1, row: i + 1, col: j + 1 }
obj.pA.push(cs[i][j]);
} else {
obj[cs[i][j]].count += 1;
}
}
})
})
let vA = [["Color Counter"]];
let cA = [["#ffffff"]];
obj.pA.forEach(p => {
cA.push([obj[p].color]);
vA.push([obj[p].count])
})
const osh = ss.getSheetByName("Sheet1");//Display Sheet
osh.clear();
osh.getRange(1, 1, vA.length, 1).setValues(vA);
osh.getRange(1, 1, cA.length, 1).setBackgrounds(cA);
}
Demo:

How can I lock editing permissions in Google Sheets using Google Scripts, based on cell color AND continent?

I can lock by color, but need to also to lock only the white cells (ffffff) that contain any content.
var ss = SpreadsheetApp.getActiveSpreadsheet(); //clears green feilds
var sheet = ss.getSheetByName("Sheet1");
var color = sheet.getRange('C23').getBackground()
var range = sheet.getDataRange();
var bgColors = range.getBackgrounds();
var data = range.getValues();
var formulas = range.getFormulas(); // Added
for (var i = 0; i < bgColors.length; i++) {
for (var j = 0; j < bgColors[i].length; j++) {
if (bgColors[i][j] == color) {
data[i][j] = '';
}
}`enter code here`
}
formulas.forEach((r, i) => r.forEach((c, j) => { // Added
if (c) data[i][j] = c;
}));
range.setValues(data).setBackgrounds(bgColors);//reset values and backgrounds
If I understand correctly what you need is to protect cells that have the same color from cell C23 and also the ones that are white but don't have an empty value on them. If so, you can try something like this:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
var color = sheet.getRange('C23').getBackground()
var range = sheet.getDataRange();
var bgColors = range.getBackgrounds();
var data = range.getValues();
var formulas = range.getFormulas(); // Added
for(var i=0; i< bgColors.length; i++)
{
for(var j=0; j < bgColors[i].length; j++)
{
if((bgColors[i][j] == color)||(bgColors[i][j] == "#ffffff" && data[i][j] != "" ))
{
var protection = range.getCell(i+1, j+1).protect();
var editors = protection.getEditors();
protection.removeEditors(editors);
//protects the cell that matches the criteria and removes any editor
//so that only you have access to edit it
}
}
}
formulas.forEach((r, i) => r.forEach((c, j) => { // Added
if (c) data[i][j] = c;
}));
range.setValues(data).setBackgrounds(bgColors);//reset values and backgrounds
}
Let me know if that works for you, or if the criteria you are using is different so that I can change the script.
References:
Method: protect()

How to loop through cells in Google Sheets and log information if specific variables exist

You will need this link to solve this: - https://docs.google.com/spreadsheets/d/11PjVSWPfqOBPSej3AlQ-_T8Bws66kevR08Q2NRTmVcE/edit?usp=sharing
So this is a tricky one. I am looking to loop through the name in Sheet1 Column A, and also loop through the type in Column C. Then if Column A in Sheet1 matches the name of column A in Sheet2, AND the type is "pickup" I want to log the other information that is in Sheet 1 inside of Column C on Sheet 2. Log the things like "State", "Location", "City" next to the identical name in Sheet 2. I hope that makes sense.
I know I probably need to use a loop within a loop to do all this, but this is as far as I can get as I cannot figure out how to write the loop and then log the rows where the 2 names match.
function myFunction() {
function pullDeliveryNotes()
{
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2");
var dataRange = sheet.getDataRange();
var dataRange2 = sheet2.getDataRange();
var rng = sheet.getRange(2,1, dataRange.getLastRow()-1,dataRange.getLastColumn());
var rng2 = sheet.getRange(2,3, dataRange.getLastRow()-1,dataRange.getLastColumn());
var rng3 = sheet2.getRange(2,1, dataRange2.getLastRow()-1,dataRange2.getLastColumn());
var rngA = rng.getValues().toString()
var rngB = rng2.getValues().toString()
var rngC = rng3.getValues().toString()
for(var i = 0; i < rng.length; i++) {
for (var x = 0; x < rng2.length; x++) {
for (var y = 0; y < rng3.length; y++) {
if (rng[i][x][y].includes(rng3[i][x][y]) && rng2[i][x][y] === "pickUp") {
Logger.log("We got it")
}
}
}
}
}
}
Based on your problem statement.
Try this sample script:-
function checkValues() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const ssSource = ss.getSheetByName('Sheet1')
const ssTarget = ss.getSheetByName('Sheet2')
const sourceRange = ssSource.getDataRange().getValues().filter(r=> r[2] === 'Pickup'); // Filtering only pickup values
const targetRange = ssTarget.getDataRange().getValues();
for(var i = 0 ; i < sourceRange.length ; i++)
{
for(var j = 1 ; j < targetRange.length ; j++)
{
if(sourceRange[i][0] === targetRange[j][0]) // if name matches
{
ssTarget.getRange(`C${j+1}`).setValue(`${sourceRange[i][3]},${sourceRange[i][4]},${sourceRange[i][5]}`) // set values in column C
}
}
}
}

Using onEdit to bind a specific cell change value

This is my script. I need to populate the values in I2:I range when the cell value of C2 changes. I have created an array_temp but these are not getting pasted in I2. Can anybody please help me throw some light on it?
function onEdit(e) {
var spreadsheet = SpreadsheetApp.getActive();
var ss = e.getSheetByName('UserInterface');
var count = ss.getRange("H2").setFormula('=countif(TalukaName!B:B,UserInterface!C2)').getValue();
var DistrictName = ss.getRange('C2').getValue();
var matchindex = ss.getRange('H3').setFormula('=match(C2,TalukaName!B:B,0)').getValue();
var indexvalue = ss.getRange('H4').setFormula('=index(TalukaName!B:B,H3)').getValue();
var array = [] ;
ss.getRange('F2').clearcontent;
var ssTaluka= e.getSheetByName('TalukaName');
var range = ssTaluka.getDataRange();
var data = range.getValues();
for (var i = 0; i < count; i++) {
array[i]= data[i + matchindex - 1][0];
}
var array_temp = [];
for (var j = 0; j < array.length; j++) {
array_temp.push([
array[j]
])
}
ss.getRange('I2:I').clearContent();
ss.getRange('I2:I'+ (count+1)).setValues(array_temp) ;
}

Find and write to next blank cells in a column using App maker

I need help with finding a number from a specific row in column A and write next to its blank cells in two different columns of the same row. e.g Find a number in COLUMN A2, Write to cells in COLUMN B2 and C2.
I have currently used filters and it hasn't helped.
function filterRange(filteredRange) {
return filteredRange == [2.0];
}
function findCellByValue() {
var ss = getSpreadSheet();
var sheet = ss.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
for (var i = 0; i < values.length; i++) {
var range = values[i];
var filtered = range.filter(filterRange);
var newValue = filtered.concat(["Tee", "uhuuu"]);
console.log(newValue);
var ssheet = sheet.getRange("B2:C2");
console.log(ssheet.setValues([newValue]));
}
}
Anyways I have managed to find a workaround it though it is not the best possible solution but for this, it works.
function findCellByValue(cellValue) {
console.log(cellValue);
var ss = getSpreadSheet().getActiveSheet();
var range = ss.getRange("A2:A").getValues();
for(var i = 0; i < range.length; i ++)
{
if(range[i] == cellValue)
{
var Arrayvalues = range[i].concat(["test1","test2"]);
ss.getRange(i+2,1,1,3).setValues([Arrayvalues]);
i = range.length;
}
}