App Script COUNTIF for maximum date in column - javascript

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()

Related

Google Apps Script - Usage of "indexOf" method

first: I really tried hard to get along, but I am more a supporter than a programmer.
I put some Text in Google Calc and wanted to check the amount of the occurances of "Mueller, Klaus" (It appears 5 times within the data range). The sheet contains 941 rows and 1 Column ("A").
Here is my code to find out:
function countKlaus() {
// Aktives Spreadsheet auswählen
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Aktives Tabellenblatt auswählen
var sheet = ss.getSheetByName("Tabellenblatt1");
var start = 1;
var end = sheet.getLastRow();
var data = sheet.getRange(start,1,end,1).getValues();
var curRow = start;
var cntKlaus = 0;
for( x in data )
{
var value = daten[x];
//ui.alert(value);
if(value.indexOf("Mueller, Klaus")> -1){
cntKlaus = cntKlaus + 1;
}
}
ui.alert(cntKlaus);
}
The result message is "0" but should be "5".
Issues:
You are very close to the solution, except for these two issues:
daten[x] should be replaced by data[x].
ui.alert(cntKlaus) should be replaced by SpreadsheetApp.getUi().alert(cntKlaus).
Solution (optimized by me) - Recommended:
function countKlaus() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName("Tabellenblatt1");
const cntKlaus = sheet
.getRange('A1:A' + sheet.getLastRow())
.getValues()
.flat()
.filter(r=>r.includes("Mueller, Klaus"))
.length;
SpreadsheetApp.getUi().alert(cntKlaus);
}
You can leave out this term + sheet.getLastRow() since we are filtering on a non-blank value. But I think it will be faster to have less data to use filter on in the first place.
References:
flat : convert the 2D array to 1D array.
filter : filter only on "Mueller, Klaus".
Array.prototype.length: get the length of the filtered data
which is the desired result.
includes: check if Mueller, Klaus is included in the text.
Bonus info
Just for your information, my solution can be rewritten in one line of code if that's important to you:
SpreadsheetApp.getUi().alert(SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Tabellenblatt1").getRange('A1:A').getValues().flat().filter(r=>r.includes("Mueller, Klaus")).length);

Automate Hyperlink Creations

I'm trying to automate hyperlink creations on my GSheet.
Here's my script:
function ticketURLGenerator() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Data");
var range = sheet.getRange("C2:C");
var ticketID = range.getValue();
Logger.log(ticketID);
for(i = 0; i < ticketID.length; i++){
if(ticketID.length === 0){
ticketID.setValue('')
} else if(ticketID.length > 4){
ticketID.setValue('=HYPERLINK' + '("https://mylink.com/'+ticketID+'";'+ticketID+')');
}
}
}
It does nothing but when I change ticketID.setValue by sheet.getRange("C2:C").setValue it put the whole range in the url. We can see with Logger.log(ticketID) that the whole range is selected.
So according to this result, i'm missing how to get the value of each cell individualy in the range and then check if they are long enought to create an individual url. Do I need to use something like range[i] somewhere? I'm lost.
I believe your goal as follows.
You want to retrieve the values from the cells "C2:C".
When the length of value is more than 4, you want to create a formula of HYPERLINK.
When the length of value is less than 4, you don't want to put the formula.
You want to put the formulas to the cells "C2:C".
Modification points:
When range of var range = sheet.getRange("C2:C") is used, the value of var ticketID = range.getValue() is the value of cell "C2". When you want to retrieve values from the cells "C2:C", please use getValues instead of getValue.
In this case, the retrieved value is 2 dimensional array.
When range.getValue() is the string value, ticketID of var ticketID = range.getValue() is also the string. So I think that when ticketID.setValue('##') is run, an error occurs.
In your script, setValue is used in a loop. In this case, the process cost will become high.
And, when sheet.getRange("C2:C" + sheet.getLastRow()) is used instead of sheet.getRange("C2:C"), the process cost will become low a little.
When above points are reflected to your script, it becomes as follows.
Modified script:
function ticketURLGenerator() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Data");
var range = sheet.getRange("C2:C" + sheet.getLastRow());
var ticketIDs = range.getValues();
var values = ticketIDs.map(([c]) => [c.toString().length > 4 ? `=HYPERLINK("https://mylink.com/${c}";"${c}")` : c]);
range.setValues(values);
}
In this modification, the values are retrieved from the cells of "C2:C" + sheet.getLastRow(), and an array including the formulas and values is created, and then, the array is put to the cells.
And I used the template literal for creating the formula.
Note:
In this case, please use this script with enabling V8 runtime.
References:
getLastRow()
getValues()
map()
Template literals
You just need to apply the HYPERLINK operation to the tickets that their length is more than 4. To achieve that, you can use map() to iterate over all the elements in your list.
Solution:
function ticketURLGenerator() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName("Data");
const ticketR = sheet.getRange("C2:C"+sheet.getLastRow());
const ticketIDs = ticketR.getDisplayValues().flat();
const hLinks = ticketIDs.map(ti=>{
if(ti.length>4) {
return [`=HYPERLINK("https://mylink.com/${ti}"; ${ti})`]}
else {return [ti]}
})
ticketR.setValues(hLinks);
}

Return row number is match error

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);

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.

Fill a range (multiple columns) down multiple rows - google-apps-script

I had success filling a single column (A) with the value found in range A1...
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var lastRow = ss.getDataRange().getNumRows();
var rngVal = ss.getRange("A1").getValue()
ss.getRange("A2:A"+lastRow).setValue(rngVal)
So then I thought I was on easy-street, and I tried to modify/apply that to a larger range by filling a multi-column range with the values found in range C1:H1...
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var lastRow = ss.getDataRange().getNumRows();
var rngVal = ss.getRange("C1:H1").getValues()
ss.getRange("C2:H"+lastRow).setValues(rngVal)
Apparently there is a bit more to this than simply slapping an "S" onto the end of the word "Value".
The error reads as follows:
Incorrect range height, was 1 but should be 10
(FYI: var lastRow = 11)
Btw, I get no error if I use Value instead of Values, although I end up with cells full of the value found only in range C1.
So I'm close.... or way off. One of those.
Help???
The error message is quite explicit... the size of the array must fit into the range in both getValues and setValues. Try it like this
:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var lastRow = ss.getLastRow()
var rngVal = ss.getRange("C1:H"+lastRow).getValues();// get an array of 10 "rows" and 6 "columns"
ss.getRange("C2:H"+(lastRow+1)).setValues(rngVal);//write back this array to a range that has the same size. (starting from Row2 it must ends on lastRow+1 to keep the same number of "rows"
}
This function will shift the range C1:H last Row to C2:H lastRow+1, not sure it is very useful but that's not the point here ;-)
EDIT : sorry, I didn't understand exactly your requirement... here is a code that reproduce data from C1:H1 in all rows below
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var lastRow = ss.getLastRow()
var rngVal = ss.getRange("C1:H1").getValues();// get an array of the first row
var rowData = rngVal[0]
var newData = []
for(n=1;n<lastRow;++n){newData.push(rowData)}
ss.getRange("C2:H"+lastRow).setValues(newData);//write back this array to a range that has the same size. (starting from Row2 it must ends on lastRow+1 to keep the same number of "rows"
}
EDIT2 following your comment below :
A word of explanation :
When using range.getValues() we get a 2 dimensions array, meaning an array of arrays that can be represented as follow : [[data1,data2,data3],[data4,data5,data6]] data1, 2 & 3 are the value of the first array (index 0) and data 4,5 & 6 are the values of the second array (index 1). So if you want to get the values in the first array you have to write it like this : value = arrayName[0] and this will return a one dimension array [data1,data2,data3], that's what I used to get rowData.
Now we need to get a 2 dimension array again to be able to write back the new data to a range in the spreadsheet. Therefor we create a new array (var newData=[] or var newData = new Array() does exactly the same), and in the for loop we add the rowData array to this new array... the result will be an array of arrays, that is actually what we were looking for and we can write this directly to the sheet in one single setValues statement.
Ok, this seems to do it...
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var lastRow = ss.getLastRow()
var rngVal = ss.getRange("C1:H1").getValues()
for (var x=1; x<=lastRow; x++) {
ss.getRange("C"+x+":H"+x).setValues(rngVal);

Categories

Resources