How to make [Conditional Formatting] script? - javascript

I want,
If Sheet1 ColumnB = Sheet89 ColumnA
Then matched Sheet1 Column B cells will be green
Here is my demo sheet.
Based on some guideline I made this but not working.
function formatting() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var columnB = sheet.getRange(1, 2, sheet.getLastRow()-1, 1);
var bValues = columnB.getValues();
var sheet89 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet89');
var columnO = sheet89.getRange(1, 1, sheet.getLastRow()-1, 1);
var oValues = columnO.getValues();
for (var h = 0; h < bValues.length; h++) {
for (var i = 0; i < oValues.length; i++) {
if (oValues[i][0] == bValues[h][0]) {
sheet.getRange(i + 2, 1, 1, 1).setBackgroundColor('green');
}
}
}
}

This solution below will iterate through each cell with a value in column B of sheet1 and check it against every value in Column A of sheet89 (although you named this ColumnO, according to your getValues function, it will grab values from Column A).
If it finds a match, it will turn green the cell in column B of sheet1. In your example code you use the i loop variable (which iterates through rows on sheet89) to get the cell on sheet1 to turn green. It's not clear which cells you want to turn green. I assumed it was the cells on sheet1 so I changed the the code to
sheet.getRange(h+1, 2).setBackgroundColor('green');
Also, the getRange function for a single cell only requires 2 arguments, so I removed the numRows and numColumns arguments for the line which colors the cell green.
I'm not sure why bValues and oValues exclude the last row, but I removed the -1 in each of these as it will cause the code to fail if for any reason it is run on a blank worksheet. The getLastRow() returns the last row with a value, not the next blank row in the sheet. If you want to capture the whole sheet, then you shouldn't use -1.
function formatting() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var columnB = sheet.getRange(1, 2, sheet.getLastRow(), 1);
var bValues = columnB.getValues();
var sheet89 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet89');
var columnO = sheet89.getRange(1, 1, sheet.getLastRow(), 1);
var oValues = columnO.getValues();
for (var h = 0; h < bValues.length; h++) {
for (var i = 0; i < oValues.length; i++) {
if (oValues[i][0] == bValues[h][0]) {
sheet.getRange(h + 1, 2).setBackgroundColor('green');
}
}
}
}

What I understand to be required (and not what the example sheet shows at present) is possible with Conditional formatting.
In Google Spreadsheets conditional formatting across sheets is not nearly as straightforward as within a single sheet, because of security and therefore authorisation. You may, for speed for example, prefer to copy the content of Sheet89 into Sheet1 (just two cells) to avoid that issue, or indeed write a script instead. At least keep the ranges as small as practical.
However it is possible, though may be slow and require authorisation.
Please clear any conditional formatting from Sheet1 ColumnA then:
Select ColumnA in Sheet1, Format, Conditional formatting..., Format cells if... Custom formula is and
=countif(IMPORTRANGE(" k e y ","Sheet89!A:A"),A1)<>0
with highlighting of your choice and Done.
k e y above represents the unique identification code for Sheet89 (will look something like 1u4vq8vDne-aKMVdJQPREGOxx7n99FqIb_kuJ_bG-PzM).
The image shows at present what is in ColumnC of the image (but is in ColumnA of the example) and F1 and F2 in the image show what is in ColumnA of Sheet89 of the example. The paler brown has been applied with Conditional formatting::

Related

How to paste values from one sheet to another to last row of specific column

A novice on app scripts, but managed to successfully build my own script through much research, but now my script is running into errors. Below is my current code:
function MyFunction() {
var sss = SpreadsheetApp.getActive();
var ss = sss.getSheetByName('Daily Sales');
var range = ss.getRange('B8:H83');
var data = range.getValues();
var OUrange = ss.getRange('K8:Q83');
var OUdata = OUrange.getValues();
var ts = sss.getSheetByName('Tracker');
ts.getRange(ts.getLastRow()+1, 1,data.length, data[0].length).setValues(data);
ts.getRange(ts.getLastRow()+1, 1,OUdata.length, OUdata[0].length).setValues(OUdata);
}
In the Daily Sales sheet I am copying values from columns B-H and K-Q and pasting them in the last row of the Tracker sheet starting at Column A. The Daily Sales values in Col. K-Q are pasted correctly below the B-H values, so happy with those results.
On the Tracker sheet these values are in Columns A-G. However I have since added formulas in Columns I and J based on the script data and a manual entry in Column H. These formulas are copied down the entire column within the sheet (i.e. row 5000). Since adding these formulas, the script is now pasting values in A5001.
I realize it is because of these formulas, but is there a way to update my script to paste in the last row of column A while maintaining those formulas in Columns I and J? Thanks in advance.
You could create a helper function that computes the first free row of a given column (NOT a column with formulas)
function MyFunction() {
var sss = SpreadsheetApp.getActive();
var ss = sss.getSheetByName('Daily Sales');
var range = ss.getRange('B8:H83');
var data = range.getValues();
var OUrange = ss.getRange('K8:Q83');
var OUdata = OUrange.getValues();
var ts = sss.getSheetByName('Tracker');
var values = data.concat(OUdata).filter(function (r) {return r[0]});
var fr = findFirstFreeRow(ts, 1) // second parameter represents column to check for first free row
ts.getRange(fr, 1,values.length, values[0].length).setValues(values);
}
function findFirstFreeRow(sheet, colNum) {
var v = sheet.getRange(1, colNum, sheet.getLastRow()).getValues(),
l = v.length,
r;
while (l >= 0) {
if (v[l] && v[l][0].toString().length > 0) {
r = (l + 2);
break;
} else {
l--;
}
}
return r;
}
See if that helps?
After deleting the extra empty rows from your 'Tracker' sheet, you can use the appendRow function to create a new row with the manual values (leaving the cells that need formulas blank).
After the manual values are in, you can then get the cells that need formulas and use setFormula on them.
For the sake of brevity, if you wanted column A,B,C,E,F to have manual values and column D to have a formula you would do:
var sheet = SpreadsheetApp.getActiveSheet();
sheet.appendRow(['A','B','C','','E','F']);
sheet.getRange(sheet.getLastRow(),4).setFormula('=FORMULA(HERE)');

Add/Subtract Inventory Data in MULTIPLE columns, not just ONE column

What should I do to change the code below (at the very bottom) so that it works for multiple column pairs, not just one column pair?
I am tracking inventory in a sheet, something like this:
ColA ColB ColC ColD ColE ColF ColG ColH
Category Item R+/- G+/- B+/- Red Green Blue
AAA A 1 0 0
AAA B 2 1 0
I want to be able to type numbers into ColC, ColD and ColE and then click a button to subtract those numbers from the totals in ColF, G and H, respectively.
I found a similar question with a great answer here, for ONE column pair:
Google Sheets - How to create add and subtract buttons to track inventory. The code got me started. I'm pretty sure I needed to update the getRange stuff from what it was (shown immediately below) to what is now listed in the whole function code further down. (I also changed some names/variables to better match my inventory needs.)
var updateRange = sheet.getRange(2, 3, maxRows); // row, column, number of rows
var totalRange = sheet.getRange(2, 4, maxRows);
But what do I do with the for section so that it works for all three column pairs, not just for ColC & ColF? I tried adding a "var col in updateValues" but it didn't like col (or column); besides i wasn't sure how to nest it with the var row that's already there. (I did notice that if I changed the 0 after each [row] to 1, it would do the 2nd columns. But it didn't like it when I did "var col in updateValues" and then "updateValue[0][col]".)
function subtractUpdateBulk() {
var sheet = SpreadsheetApp.getActiveSheet();
var maxRows = sheet.getMaxRows();
var updateRange = sheet.getRange(2, 3, maxRows, 3); // row, column, # of rows, # of cols
var totalRange = sheet.getRange(2, 6, maxRows, 3);
var updateValues = updateRange.getValues();
var totalValues = totalRange.getValues();
for (var row in updateValues) {
var updateCellData = updateValues[row][0];
var totalCellData = totalValues[row][0];
if (updateCellData != "" && totalCellData != "") {
totalValues[row][0] = totalCellData - updateCellData;
updateValues[row][0] = "";
}
}
updateRange.setValues(soldValues);
totalRange.setValues(totalValues);
}
If you offer code alone is great. A bit of explanation to go with it would be even better, so I understand the WHY and can hopefully apply it elsewhere.
Your code was pretty close.
This is the substitute code to manage three columns of data movements and three columns of totals. Most of the code is self-explanatory but I'll focus on a couple of points. Its good that you're interested in the why as well as the how, and I've left some DEBUG lines that hopefully with assist.
1) I setup the spreadsheet and sheet using "standard" commands. In this case, I used getSheetByName to ensure that the code always would execute on the desired sheet.
2) I didn't use getMaxRows because this returns "the current number of rows in the sheet, regardless of content.". So if your spreadsheet has 1,000 rows but you've only got, say, 20 rows of data, getmaxRows will return a value of 1,000 and force you to evaluate more rows than are populated with data. Instead I used the code on lines 30 and 30 var Avals and var Alast which use a javascript command to quickly return the number of rows that have data. I chose Column A to use for this, but you could change this to some other column.
3) Rather than declare and get values for two ranges (updateRange and totalRange), I declared only one data range totalRange and got the values for all 6 columns. getValues is a fairly time costly process; by getting values for all rows and all columns, you can then pick and choose which columns you want to add together.
The command is:
var totalRange = sheet.getRange(NumHeaderRows + 1, 3, Alast - NumHeaderRows, 6);
The syntax (as you noted) is row, column, # of rows, # of cols.
The start row is the row following the header => (NumHeaderRows + 1).
The start column is Column C => 3.
The number of rows is the data rows less the header rows => (Alast - NumHeaderRows)
The number of columns is ColC, ColD, ColE, ColF, ColG => 6
4) for (var row in totalValues) {
This was a new one for me, and its so simple, so I keep it.
5) I used two arrays, just as you did. I used one array (RowArray) to build the values for each row, and the second array (MasterArray)is cumulative.
BTW, in your code soldValues isn't ever declared and no values are ever assigned to it.
6) The most important thing is the calculation of the adjustments on each line:
For the sake of clarity, I declared three variables totalRed, totalGreen and totalBlue, and showed how the totals were calculated for each value. This wasn't strictly necessary (I could have just pushed the formulas for each new totals), but they enable you to how each movement is calculated, and the column numbers used in each case.
function so_53505294() {
// set up spreadsheet
// include getsheetbyname to ensure calculations happen on the correct sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
//set some variables
var NumHeaderRows = 1; // this is the number of header rows - user changeable
var totalRed = 0;
var totalGreen = 0;
var totalBlue = 0;
// arrays used later in loop
var RowArray = []; // row by row values
var MasterArray = []; // row by row, cumulative values
// get number of rows of data
var Avals = ss.getRange("A1:A").getValues();
var Alast = Avals.filter(String).length;
// Logger.log("the last row = "+Alast);// DEBUG
// define the entire data range
var totalRange = sheet.getRange(NumHeaderRows + 1, 3, Alast - NumHeaderRows, 6); // row, column, # of rows, # of cols
//Logger.log("the TotalRange = "+totalRange.getA1Notation());//DEBUG
// get the data fior the entire range
var totalValues = totalRange.getValues();
// loop through thr rows
for (var row in totalValues) {
// clear RowArray at the start of each new row
RowArray = [];
// calculate the new totals
totalRed = totalValues[row][0] + totalValues[row][3];
totalGreen = totalValues[row][1] + totalValues[row][4];
totalBlue = totalValues[row][2] + totalValues[row][5];
//Logger.log("row = "+row+", Movement RED = "+totalValues[row][0]+", Old Stock RED = "+totalValues[row][3]+", New RED = "+totalRed); //DEBUG
//Logger.log("row = "+row+", Movement GREEN = "+totalValues[row][1]+", Old Stock GREEN = "+totalValues[row][4]+", New GREEN = "+totalGreen); //DEBUG
//Logger.log("row = "+row+", Movement BLUE = "+totalValues[row][2]+", Old Stock BLUE = "+totalValues[row][5]+", New BLUE = "+totalBlue); //DEBUG
// update the RowArray for this row's values
RowArray.push(0, 0, 0, totalRed, totalGreen, totalBlue);
// update the MasterArray for this row's values
MasterArray.push(RowArray);
}
// Update the data range with the new Master values.
totalRange.setValues(MasterArray);
}

How to make for loop script more efficient?

I'm very new to script writing in general, especially in GAS, and want to make sure I learn good habits. I wrote a for loop script that, in essence, does the following:
Read through column A starting at row 2
If cell above current cell in for loop is the same value, then clear the contents of the adjacent cell to the right of the current cell.
For example
Cell A1 contains "Something". Cell B1 contains "Exciting"
Cell A2 contains "New". Cell B2 contains "X"
Cell A3 contains "New". Cell B3 contains "Y"
Since A3 has the same value as cell A2 (that value being "New"), cell B3 (value currently "Y") is cleared so that there is no value in cell B3.
It seems to take a very long time to run, which I am sure is due to my novice writing, and I want to make this script as efficient as possible.
Do any of you scripting gods have any suggestions to make this script more efficient?
I would also appreciate any explanation as to why any suggestion would be more efficient for my own understanding, and so that anyone that happens to find this posting, later on, can understand it as well.
Here is the script:
function testCode() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Test 1");
var source = sheet.getRange("A2:A");
for (i = 2; i < source.getLastRow(); i++) {
var currentCell = sheet.getRange(i,1).getValue();
var cellAbove = sheet.getRange(i-1,1).getValue();
var cellRight = sheet.getRange(i,2);
if (currentCell == cellAbove){
cellRight.clearContent(),({contentsOnly:true});
}
}
}
THANK YOU
The biggest issue is that you are getting three new ranges in every loop. There is a way to do this without getting new ranges in the loop. You'll need to get the data in both columns A and B.
function testCode() {
var cellAbove,currentCell,cellRight,data,lastRow,L,sheet,source,ss;
ss = SpreadsheetApp.getActiveSpreadsheet();
sheet = ss.getSheetByName("Test 1");
lastRow = sheet.getLastRow();
source = sheet.getRange(2,1,lastRow-1,2);//Get data starting in row 2 and column 1
//Get the number of rows that are the lastRow minus the number of rows not included
//at the top - get 2 columns
data = source.getValues();//Get a 2D array of values
L = data.length;//Get the number of elements in the outer array- which is the number of
//rows in the array
for (var i = 0; i < L; i++) {
if (i == 0) {continue;} //If this is the first loop there is no value above it to check
currentCell = data[i][0];
cellAbove = data[i-1][0];
if (currentCell == cellAbove){
cellRight = sheet.getRange(i+1,2);
cellRight.clearContent(),({contentsOnly:true});
}
}
}

Use Google Apps Script to loop through the whole column

I am trying to loop through the whole row in my google sheet and copy some of the data from one sheet to another. The list will get longer over time.
More specifically: If input in column B equals "blue", than copy the values from column A and C into another sheet.
Do this for all columns till the end of the column.
Link to my spreadsheet: https://docs.google.com/spreadsheets/d/1xnLygpuJnpDfnF6LdR41gN74gWy8mxhVnQJ7i3hv1NA/edit?usp=sharing
The loop stops when the colour does not equal blue. Why?
As you can see I used a for loop. Is that even the way to go?
Can I do anything about the speed of the code execution?
Any comments, hints or help are highly appreciated.
Regards!
You had the input sheet named "List" and I named the output sheet "Output". And here's the code.
function condCopy()
{
var s = SpreadsheetApp.getActiveSpreadsheet();
var sht = s.getSheetByName('List')
var drng = sht.getDataRange();
var rng = sht.getRange(2,1, drng.getLastRow()-1,drng.getLastColumn());
var rngA = rng.getValues();//Array of input values
var rngB = [];//Array where values that past the condition will go
var b = 0;//Output iterator
for(var i = 0; i < rngA.length; i++)
{
if(rngA[i][1] == 'blue')
{
rngB[b]=[];//Initial new array
rngB[b].push(rngA[i][0],rngA[i][2]);
b++;
}
}
var shtout = s.getSheetByName('Output');
var outrng = shtout.getRange(2,1,rngB.length,2);//Make the output range the same size as the output array
outrng.setValues(rngB);
}
You have 2 options. The first is to use the standard query() function from Google Sheets to get the values. The downside here is that it is only a reference of the values. So you cannot reorder them, etc. To use this, place this in cell A1 and it will pull the Headers and retrieve the values from column A and C:
=QUERY(A:C, "select A, C where B = 'blue'", 1)
For a Google Apps Script answer:
This will loop through your List sheet and for every row where column B is blue it will save the values in column A and C to column A and B of the new sheet:
function doIt(){
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet4");
var lastRow = activeSheet.getLastRow();
var lastCol = activeSheet.getLastColumn();
var targetValues = [];
var sourceSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("List");
var lastSourceRow = sourceSheet.getLastRow();
var lastSourceCol = sourceSheet.getLastColumn();
var sourceRange = sourceSheet.getRange(1, 1, lastSourceRow, lastSourceCol);
var sourceData = sourceRange.getValues();
var activeRow = 0;
//Loop through every retrieved row from the Source
for (row in sourceData) {
//IF Column B in this row has 'blue', then work on it.
if (sourceData[row][1] === 'blue') {
//Save it ta a temporary variable
var tempvalue = [sourceData[row][0], sourceData[row][2]];
//then push that into the variables which holds all the new values to be returned
targetValues.push(tempvalue);
}
}
//Save the new range to the appropriate sheet starting at the last empty row
activeSheet.getRange(lastRow + 1, 1 , targetValues.length, 2).setValues(targetValues);
}
Of course, you could pass the value to test to the function by replacing 2 lines. The first, defining the function:
function doIt(testingvar){
to pass a variable called testingvar, and the test line to replace the hard coded test with the passed variable:
if (sourceData[row][1] === testingvar) {

Google Sheets Script - Loop values in different cells

I come from VBA and Python so still trying to get the hang of javascript in Google Sheets. I am trying to do something pretty simple.
Find last row in column D - Sucessful
Name array in Column D from row 2 thru LastRow - Sucessful
Copy value of cell for row i and paste in column E - Not Successful
I need it to look in data[0][0] and copy the info in column E, then look in data[1][0] and copy info in column E, etc., etc. up to the lastRow. Everything works up till the last line and I get an error saying the "The coordinates or dimensions of the range are invalid."
Here is my code: (FYI: lastrow() is a function that finds last row # in column specified)
function GatherStoreInfo() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ReportSheet = ss.getSheetByName('Report');
for (var i = 0; i < lastRow('A') + 1; i = i+1) {
var data = ReportSheet.getRange(2, 4, lastRow('D'), 1).getValues();
ReportSheet.getRange(i, 2).setValue(data[i][0]); // <- This doesn't work
}
}
Ok after messing around with it more I figured it out. I guess I'll post the answer just in case someone stumbles on this.
Basically it was because I was starting i at 0 and then in the range I was telling it to start at row 0 which doesnt exist, so I had to add i + 1 in the range and it worked. Here's the final code:
function GatherStoreInfo() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ReportSheet = ss.getSheetByName('Report');
for (var i = 0; i < lastRow('A'); i = i+1) {
var data = ReportSheet.getRange(1, 4, lastRow('D'), 1).getValues();
SpreadsheetApp.getActiveSheet().getRange(i + 1, 5).setValue(data[i]);
}
}

Categories

Resources