Return row number is match error - javascript

I have some tables in one sheet and I need to make a script to send an email everyday. The script it search today date, match it into my table and return column number, then I looks for Total on the first column (A) and once found it, return row number. Once I have row number and column number, return Total value for that day. I'm not advanced with JavaScript and I'm struggle with arrays (still learning). The script I have so far is working very good as long I have only one table on that sheet, but on the sheet will be over 50 tables, each one will have a Total at the end. The formula I have will find Total, but will return all Totals (row numbers) as Strings. What do I need is to get just the first Total (row number). I hope it all make sense.
I have attached an image to make an idea and my script I have so far:
function getTodaysTotal() {
function toDateFormat(date) {
try {return date.setHours(0,0,0,0);}
catch(e) {return;}
}
var values = SpreadsheetApp
.openById("ID")
.getSheetByName("Q3 - W27 - 39")
.getDataRange()
.getValues();
for (i in values){
if (values[i][0]=='Total'){
var nr = i;
Logger.log(nr); // will return two values (41 - first total and
104 second total ... if you add more Total it will return all rows
numbers that contain word Total
}
}
var today = toDateFormat(new Date());
var todaysColumn =
values[5].map(toDateFormat).map(Number).indexOf(+today);
var output = values[nr][todaysColumn];
// Logger.log(output);
var emailDate = Utilities.formatDate(new Date(today),"GMT+1",
"dd/MM/yyyy");
This is just the first table, but there will be more under this one and each one will have a Total.
Thank you!
Kind regards!

You should declare nr outside of your loop since you will use the value.
var nr = 0;
Since you are reading an array, you should use the array length for you loop
for (var i=0; i<values.length; i++){
if (values[i][0]=='Total'){
nr = i;
Logger.log(nr);
break; // this will stop at the first match
}
}
Once you get your total as a String, you can convert it to a number by calling the following function.
var string = values[a][b];
var num = Number(string);

Related

App Script COUNTIF for maximum date in column

I have a list of dates in column A:
Column A
2022-02-28
2022-02-28
2022-02-28
2022-02-14
2022-02-14
2022-02-07
I'm trying to write a script that counts the number of times the largest date occures. I wrote the below script
function maxcount() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var devdeploy = ss.getSheetByName("Sheet1")
var Avals = devdeploy.getRange("A2:A").getValues();
var Alength = Avals.filter(String).length;
var max = Avals[0][0]
var unique_count = 0
for (i=0; i < Alength; i++){
if (Avals[i][0] == max){
unique_count++;
}
}
Logger.log(unique_count)
}
This script works if I use integer values and have the maximum value in cell A2. However, when I use dates instead of integers it always returns a value of 1. Any ideas on why the if loop does not work on dates, but works on integers/strings? Also is there a way to improve the script to look for the maximum value in column A then find how many times it occurs?
If your date in the column "A" is the date object, how about the following modification? I thought that in your script, by var max = Avals[0][0], only 1st element is compared. And, if the values of column "A" are the date object, the date object is compared. I thought that this might be the reason for your issue.
Modified script:
function maxcount() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var devdeploy = ss.getSheetByName("Sheet1")
var Avals = devdeploy.getRange("A2:A" + devdeploy.getLastRow()).getValues().filter(([a]) => a.toString() != "");
var values = Avals.map(([a]) => a.getTime());
var max = Math.max(...values);
var unique_count = values.filter(v => v == max).length;
console.log(unique_count)
}
In this modification, the values are converted to the unix time, and retrieved the maximum value. And, the number of the maximum value can be retrieved.
References:
map()
filter()

Sum all rows in a column function using google app scripts

I have a column "Net Sales" and I am trying to sum all the values in that column in google sheets but using google app scripts. The data for Net Sales will change so I am trying to be as abstract as possible. Here is the function I have so far, the output of the total sum is shown in a separate spreadsheet. Instead of actually adding up all of the sales, this function just puts all the numbers together. For example, if the rows in column Net Sales are 100, 200, and 50, the output would be 10020050 instead of 350. How do I get a function to actually add the numbers together?
//sum of all net sales (not working)
var netSquare = sheet_origin2.getRange(2, 12, sheet_origin2.getLastRow(), 1).getValues();
var sum = 0;
for (var i=0; i<=sheet_origin2.getLastRow(); i++) {
sum += netSquare[i];
}
sheet_destination.getRange(sheet_destination.getLastRow(), 2, 1, 1).setValue(sum);
The last row ≠ the number of rows, especially since you're skipping the first row.
.getValues() returns a 2-d array, so you need to use netSquare[i][0]
You should use the length of the array you're iterating over in your for-loop and also be sure that your index doesn't go out-of-bounds.
function sum() {
// ... define the sheets ...
var lastRow = sheet_origin2.getLastRow();
var numRows = lastRow - 1; // Subtract one since you're skipping the first row
var netSquare = sheet_origin2.getRange(2, 12, numRows, 1).getValues();
var sum = 0;
for (var i=0; i<netSquare.length; i++) {
sum += netSquare[i][0];
}
sheet_destination.getRange(sheet_destination.getLastRow(), 2, 1, 1).setValue(sum);
}
A way more efficient way to calculate the sum is to use reduce and in this way you get rid of for loops.
The sum can be calculated with just the reduce function. All the other functions: flat, map, filter are used to make sure the data is correct since we don't know how your spreadsheet file is constructed and what are the values you are using. See the code comments for detailed explanation of every step.
Solution:
const netSquare = sheet_origin2.getRange('L2:L').getValues(). // get column L (12th column)
flat(). // convert the 2D array to 1D array
filter(v=>v!=''). // filter out empty cells
map(v=>parseInt(v)); // convert string numbers to integers
const sum = netSquare.reduce((a,b)=>a+b); // add all numbers together
sheet_destination.getRange(sheet_destination.getLastRow(), 2, 1, 1).setValue(sum);
The shortest fix for this one would be to type cast since they became string when you fetched the data.
change your:
sum += netSquare[i];
to:
sum += parseInt(netSquare[i]); // if whole number
sum += parseFloat(netSquare[i]); // if number has decimal
This forces the netSquare[i] value to be in type integer/float which can be added as numbers.
There will no be issues when we are sure that netSquare[i] values are all numbers.
For the possible issues, you can check possible outcomes when type casting a non-number data.

Is it possible to pull different data sets from one column?

I've been trying to write some code that looks down one column with strings based on some simple formulas. I can't seem to get it to recognize the different sets of data and paste them where I want them.
I have tried re writing the code a few different ways in which is looks at all the data and just offsets the destination row by 1. But it does not recognize that it is pull different data.
Below is the code that works. What it does is starts from the 1st column 2nd row (where my data starts). The data is a list like;
A
1 Customer1
2 item1
3 item2
4 Item3
5
6 Customer2
7 Item1
The formulas that I have in those cells just concatenates some other cells.
Using a loop it looks through column A and find the blank space. It then "breaks" whatever number it stops on, the numerical A1 notation of the cell, it then finds the values for those cells and transposes them In another sheet in the correct row.
The issue I am having with the code this code that has worked the best is it doesn't read any of the cells as blank
(because of the formulas?) and it transposes all to the same row.
function transpose(){
var data = SpreadsheetApp.getActiveSpreadsheet();
var input =data.getSheetByName("EMAIL INPUT");
var output = data.getSheetByName("EMAIL OUTPUT");
var lr =input.getLastRow();
for (var i=2;i<20;i++){
var cell = input.getRange(i, 1).getValue();
if (cell == ""){
break
}
}
var set = input.getRange(2, 1, i-1).getValues();
output.getRange(2,1,set[0].length,set.length) .
.setValues(Object.keys(set[0]).map ( function (columnNumber) {
return set.map( function (row) {
return row[columnNumber];
});
}));
Logger.log(i);
Logger.log(set);
}
What I need the code to do is look through all the data and separate the sets of data by a condition.
Then Transpose that information on another sheet. Each set (or array) of data will go into a different row. With each component filling across the column (["customer1", "Item1","Item2"].
EDIT:
Is it Possible to pull different data sets from a single column and turn them into arrays? I believe being able to do that will work if I use "appendrow" to tranpose my different arrays to where I need them.
Test for the length of cell. Even if it is a formula, it will evaluate the result based on the value.
if (cell.length !=0){
// the cell is NOT empty, so do this
}
else
{
// the cell IS empty, so do this instead
}
EXTRA
This code takes your objective and completes the transposition of data.
The code is not as efficient as it might/should because it includes getRange and setValues inside the loop.
Ideally the entire Output Range could/should be set in one command, but the (unanswered) challenge to this is knowing in advance the maximum number rows per contiguous range so that blank values can be set for rows that have less than the maximum number of rows.
This would be a worthwhile change to make.
function so5671809203() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var inputsheetname = "EMAIL_INPUT";
var inputsheet = ss.getSheetByName(inputsheetname);
var outputsheetname = "EMAIL_OUTPUT";
var outputsheet = ss.getSheetByName(outputsheetname);
var inputLR =inputsheet.getLastRow();
Logger.log("DEBUG: the last row = "+inputLR);
var inputrange = inputsheet.getRange(1, 1,inputLR+1);
Logger.log("the input range = "+inputrange.getA1Notation());
var values = inputrange.getValues();
var outputval=[];
var outputrow=[];
var counter = 0; // to count number of columns in array
for (i=0;i<inputLR+1;i++){
Logger.log("DEBUG: Row:"+i+", Value = "+values [i][0]+", Length = "+values [i][0].length);
if (values [i][0].length !=0){
// add this to the output sheet
outputrow.push(values [i][0]);
counter = counter+1;
Logger.log("DEBUG: value = "+values [i][0]+" to be added to array. New Array Value = "+outputrow+", counter = "+counter);
}
else
{
// do nothing with the cell, but add the existing values to the output sheet
Logger.log("DEBUG: Found a space - time to update output");
// push the values onto an clean array
outputval.push(outputrow);
// reset the row array
outputrow = [];
// get the last row of the output sheet
var outputLR =outputsheet.getLastRow();
Logger.log("DEBUG: output last row = "+outputLR);
// defie the output range
var outputrange = outputsheet.getRange((+outputLR+1),1,1,counter);
Logger.log("DEBUG: the output range = "+outputrange.getA1Notation());
// update the values with array
outputrange.setValues(outputval);
// reset the row counter
counter = 0;
//reset the output value array
outputval=[];
}
}
}
Email Input and Output Sheets

Loop not working for column match

I have a loop that work to match row value from bottom and it goes like this:-
var lastRow = s3.getLastRow();
var dataRange = s3.getRange(1, 1,lastRow).getValues();
for(var k=0;k<dataRange.length;k++)
{doing something}
However, I am getting no result when I am trying to do the same thing with column match, here is my loop for column match that does not do anything.
var lastColumn = s3.getLastColumn();
var match2 = s3.getRange(1, 1,lastColumn).getValues();
for (var b = 0; b < match2.length; b++)
{if (range[j][0] == match2[0][b])
{ do something }
}
Please suggest what I am missing.
This is taken right out of the documentation:
getRange(row, column, numRows) Range Returns the range with the top left cell at the given coordinates, and with the given number of rows.
match2.length is the number of rows in the array or the range.
This array [[x,x,x],[y,y,y],[z,z,z]...] has three x's in the first row, 3 y's in the second row and so on. So in s3.getRange(1, 1,lastColumn).getValues(); lastColumn is the number of rows in that range. Essentially it's easier to read each row and then get the column one at a time. Or you could transpose your data like a matrix and then read the columns that are now rows.
A loop looking for "Big Macs':
function myFunction()
{
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var rg=ss.getRange("A1:Z1");
var vA=rg.getValues();
for(var i=0;i<vA.length;i++)
{
for(j=0;j<vA[0].length;j++)
{
if(vA[i][j]=="Big Mac")
{
SpreadsheetApp.getUi().alert('Do not eat this burger as it has massive amounts of fat in it.');
break;
}
}
}
}
In these two dimensional arrays obtained by commands such as var data = range.getValues(); data.length = the number of rows and data[0].length = the number of columns. So total number of array elements data.length x data[0].length some of which may be null. Many programmers new to Google Apps Scripting have problems in this area. In fact I had a lot of trouble with it so I ended up doing some extra work to help bolster my understanding and you can read about it here.
These arrays look like the following: [[0,1,2,3,4,5...],[0,1,2,3,4,5...],[0,1,2,3,4,5...]...]. So vA is an array of arrays and so the term vA.length is equal to the number of elements in vA and simply put it's also equal to the number rows.

Google Apps Script counting number of identical cells in 2 columns

So Im have 2 columns in google spreadsheet, A and B, and I want to compare them row by row (A1 to B1, A2 to B2, etc), and finally count the number of cells that has the exact same value (can be a string or integer, but have to be identical) and put it in another cell, D1 for example. This is what I got so far, but it doesnt seem to do anything, and doesnt return any error either.
function compare() {
var sss = SpreadsheetApp.getActiveSpreadsheet();
var ss = sss.getSheetByName('theSheetINeed');
var range1 = ss.getRange('A1:A'); //the first column
var data1 = range1.getValues();
var range2 = ss.getRange('B2:B'); //the second column
var data2 = range2.getValues();
var count = []; //to count the number of match
for(var i =0;i 'smaller than' data1.length; i++){ //somehow i cant use '<'
var abc = data1[i];
var def = data2[i];
if(abc == def){
count += count;
};
};
ss.getRange('D1').setValue(count.length);
}
Edit: so my code actually does something, it returns 0 everytime...
Modification points :
Values retrieved by getValues() are 2 dimensional array.
count is defined as an array. But it is not used as array.
i 'smaller than' data1.length is i<data1.length.
Starting row for column A and B are 1, 2, respectively.
Cells without values are included. So when such cells each other are compared, the values become the same. (If you want to compare such cells, please remove && abc && def from following script.)
Modified script :
Your script can be written by modifying above points as follows.
function compare() {
var sss = SpreadsheetApp.getActiveSpreadsheet();
var ss = sss.getSheetByName('theSheetINeed');
var range1 = ss.getRange('A1:A'); //the first column
var data1 = range1.getValues();
var range2 = ss.getRange('B2:B'); //the second column
var data2 = range2.getValues();
var count = []; //to count the number of match
for(var i=0; i<data1.length-1; i++){ //somehow i cant use '<'
var abc = data1[i][0];
var def = data2[i][0];
if(abc == def && abc && def){
count.push(abc);
};
};
ss.getRange('D1').setValue(count.length);
}
If I misunderstand your question, I'm sorry.
An alternative to an Apps Script needing to run in the background you can do this with out-of-the-box formulas, and therefore the result is live
=SUM(
QUERY(
FILTER(A1:B,A1:A<>"",A1:A<>0),
"Select count(Col1)
Where Col1=Col2
Group By Col1
Label count(Col1) ''"
,0)
)
The advantage of formula based solutions is that they are more visible, and anyone following you can be sure the answer is correct without knowing they have to run a script to achieve this.
Breaking the formula down and starting in the middle:
FILTER(A1:B, A1:A<>"", A1:A<>0)
this returns all the rows where there is a non-empty cell. I do this because QUERY can be misleading with finding blank cells
"Select count(Col1)
Where Col1=Col2
Group By Col1
Label count(Col1) ''"
This does the comparisons you asked for and returns a count for each of the values in A that have a match in B. The Select here uses Col1 instead of A because the FILTER returns an Array not a
Range.
From then the SUM adds up each of those totals.

Categories

Resources