loop count number of consecutive cell containing specific word - loops

I'm quite a beginner in JavaScript (well google app script) but now I can manage my work without it. My goal is to send an email to someone when the number of cell containing the word "alert" is equal to 3. I think that the structure of my program might be correct however the syntax may be wrong.
Let me show you what does my program look like for now (I want it to begin on row number 24 and on the 34th column of my sheet):
function onEdit(e) {
let sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
//EVENT VARIABLES
let range = e.range;
let row = e.range.getRow();
let col = e.range.getColumn();
let cellValue = sheet.getActiveCell().getValue();
let lastRow = sheet.getLastRow;
let projectName = sheet.getRange(row,1).getValue();
let user = Session.getActiveUser().getEmail();
let cellLocation = sheet.getActiveCell().getA1Notation();
let countAlerte = 0;
let url = "https://docs.google.com/spreadsheets/d/blablabla"
for (i=24;i<=lastRow;i++){
if ((col == 34 && cellValue == "alerte")){
countAlerte = 1;
}
else {
countAlerte = 0;
}
if (countAlerte == 3){
MailApp.sendEmail(
'surname.name#xxx.com',
user + projectName + ' alerte mol 1 ',
url + '&range=' + cellLocation
);
};
}
Is my syntax and variable definition correct?
Is there something wrong with my loop?

You should add 1 instead of assignment:
{
countAlerte = countAlerte + 1; /// or just countAlerte += 1;
}
else {
countAlerte = 0;
}
That is regarding the counting.
Another thing is how do you want to catch onEdit(e). It would be better to get values in the column 34
var valuesInColumn = sheet.getRange(24, 34, lastRow, 1).getValues();
and loop through all values in the valuesInColumn array
for example:
if (col == 34){
var valuesInColumn = sheet.getRange(24, 34, lastRow, 1).getValues();
valuesInColumn.forEach(function(item){
if (item == "alerte"){
countAlerte = countAlerte + 1;
if (countAlerte == 3){
MailApp.sendEmail(
'surname.name#xxx.com',
user + projectName + ' alerte mol 1 ',
url + '&range=' + cellLocation
);
countAlerte = 0;
}
}
else{
countAlerte = 0;
}
});
}

Related

How to convert Alphanumeric Values to value ranges in Typescript?

myArray = ["AB01","AB02","AB03","AB04","AB11","BC12","BC13", "SB33"];
// expected string "AB01-AB04, AB11, BC12-BC13, SB33"
The letters can be one or two characters. Digits can be two or three characters.
Ex: A001, A002, AB001, AB002, AC01, AC02, B01, B02.
Only these formats are possible.
How to achieve this in a simplified manner?
let myArray = ["AB01","AB05","ABCDEFG01123","ABCDEFG01124","AB02","AB03","AB04","AB11","BC12","BC13", "SB33"];
function getRanges(array, group) {
var ranges = [], rstart, rend;
for (var i = 0; i < array.length; i++) {
rstart = array[i];
rend = rstart;
while (array[i + 1] - array[i] == 1) {
rend = array[i + 1];
i++;
}
ranges.push(rstart == rend ? group+rstart+'' : group+rstart + '-' + group+rend);
}
return ranges;
}
// group first.
let groups = myArray.reduce((groups,item)=>{
let belongsTo = item.match(/[a-zA-Z]/g).join('');
groups[belongsTo] ? groups[belongsTo].push(item) :groups[belongsTo]=[item];
return groups;
},{})
let expectedString = Object.entries(groups).reduce((output,[key,value])=>{
output=`${output}${output.length ? ', ' : ''}${getRanges(value.map(i=>i.match(/[0-9]/g).join('')),key)}`;
return output;
},'');
console.log(expectedString);
// expected string "AB01-AB04, AB11, BC12-BC13, SB33"

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?

use ISFORMULA with ARRAYFORMULA on a range of cells

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

Problems with includes() function and array comparsion

As a part of whole procedure, I want to compare user's input (as array) with union of two randomly given arrays. I can not use array.sort() and comparison of arrays element by element, because it's enough to have just one different element in input array and after sorting more than just one element will be identified as "wrong one" (example: array "union" after sorting = [11,13,17,18], array "upis" (user's input) after sorting = [7,11,13,18] so if I compare element by element, which I've tried in original code, all first three elements are identified as wrong...)
That's why I've switched to array.includes() and I've spend last 5.5 hours trying to find where am I wrong and just can't find it so I'm going slightly mad... In few hours I'm supposed to be at work but I'm losing my mind because of this...
THE PROBLEM IS WITHIN checkInput() FUNCTION... The code just won't and won't recognize input elements (array "upis") within "union" array... Can't figure out why???
Any help more then appreciated!
<script>
//----------- 1.) Kreiraj dva nasumična niza i uniju -----------
var arrA = []; //od 3 do max 6 članova
var arrB = []; //od 2 do max 5 članova
while (arrA.length < Math.floor(Math.random() * 4) + 3) {
var randomnumber = Math.floor(Math.random() * 20) + 1;
if (arrA.indexOf(randomnumber) > -1) continue;
arrA[arrA.length] = randomnumber;
}
while (arrB.length < Math.floor(Math.random() * 4) + 2) {
var randomnumber = Math.floor(Math.random() * 20) + 1;
if (arrB.indexOf(randomnumber) > -1) continue;
arrB[arrB.length] = randomnumber;
}
var union = [...new Set([...arrA, ...arrB])];
document.write("A = " + arrA + "<br>");
document.write("B = " + arrB + "<br>");
//----------- 2.) Funkcija za dodavanje text box-ova -----------
function addFields() {
// Broj text box-ova koje treba kreirati
var number = union.length;
// <div> u koji će se dinamično dodati text box-ovi
var container = document.getElementById("container");
// Obriši prethodni zapis u <div>
while (container.hasChildNodes()) {
container.removeChild(container.lastChild);
}
// Tekst A U B
//container.appendChild(document.createElement("br"));
container.appendChild(document.createTextNode("A ∪ B = "));
for (i = 0; i < number; i++) {
// Kreiraj <input> element i definiraj stil
var input = document.createElement("input");
input.type = "text";
input.id = "element" + i;
input.style.width = 25;
container.appendChild(input);
// Dodaj zarez poslije svakog input box-a, osim posljednjeg
if (i < number - 1) {
container.appendChild(document.createTextNode(", "));
}
}
// Pokaži gumb Provjeri
document.getElementById("provj").style.visibility = "visible";
}
//----------- 3.) Provjera upisa -----------
function checkInput() {
var upis = [];
var greske = [];
// Pohrani upis u niz
for (i = 0; i < union.length; i++) {
var privr = document.getElementById("element" + i).value;
upis.push(privr);
}
for (i = 0; i < upis.length; i++) {
// ako je neko polje nepopunjeno, obavijesti i prekini petlju
if (upis[i] === "") {
alert("Treba upisati sve članove unije skupova!");
greske = [];
//npr. prva dva upisa kriva, ostala polja nepopunjena - iako ima
//praznih polja, prekida se procedura ali se kod prva dva upisa
//popunio niz greške i onda će ih pokazati
break;
}
// u protivnom
else {
var n = union.includes(upis[i]);
alert(upis[i] + " " + n);
if (n === false) {
greske.push(upis[i]);
} else {
//ništa
}
}
}
if (greske.length > 0) {
alert("Krivo upisani članovi: " + greske);
}
}
</script>
<div id="container">
<button onclick="addFields()">Upiši članove unije</button>
</div>
<button id="provj" style="visibility:hidden" onclick="checkInput()">Provjeri</button>
You are comparing numbers with strings. Just add + to the includes parameter:
var n = union.includes(+upis[i]);
This will force the string to be unboxed as a number and your includes will work as expected.
You other option is to convert those strings to numbers and then no unboxing will be needed.

Can not save array in shared object as3

I have this code in my simple flash. I want to save name and score in my quiz. My code reference in this website http://www.mollyjameson.com/blog/local-flash-game-leaderboard-tutorial/
I want to make my code in just one actionscript. But I didn't success do it.
var m_FlashCookie = SharedObject.getLocal("LeaderboardExample");
var EntryName:String ="nama kamu";
var EntryScore:String ="nilai";
const NUM_SCORES_SAVED:int = 10;
inputBoxScore.text = EntryScore;
inputBoxName.text = EntryName
var latest_score_object:Object = {
name: EntryName,
score: EntryScore
};
var arr:Array;
arr = m_FlashCookie.data.storedArray
if ( arr == null)
{
arr = new Array();
}
arr.push( latest_score_object );
arr.sortOn("score", Array.NUMERIC | Array.DESCENDING);
if ( arr.length < NUM_SCORES_SAVED )
{
arr.pop();
}
btnSave.addEventListener(MouseEvent.CLICK, saveData);
function saveData(event:Event):void
{
m_FlashCookie.data.arr = arr;
m_FlashCookie.flush();
}
var myHTMLL:String = "";
var total_stored_scores:int = arr.length;
btnLoad.addEventListener(MouseEvent.CLICK, loadData);
function loadData(event:Event):void
{
for (var i:int = 0; i < total_stored_scores; ++i)
{
// loop through every entry, every entry has a "name" and "score" field as that's what we save.
var leaderboard_entry:Object = arr[i];
// is this the last score that was just entered last gamestate?
if ( leaderboard_entry == latest_score_object )
{
myHTMLL += (i+1) + ". <b><font color=\"#0002E5\">"+ leaderboard_entry.name + " " + leaderboard_entry.score +"</font></b><br>";
}
else
{
myHTMLL += (i+1) + ". "+ leaderboard_entry.name + " " + leaderboard_entry.score +"<br>";
}
}
myHTML.text = myHTMLL;
}
Can anybody help me?
You're saving the array as data.arr but reading the array as data.storedArray. You need to make them the same.
In other words, you've written this:
m_FlashCookie.data.arr = arr;
And when you load:
arr = m_FlashCookie.data.storedArray;
This clearly doesn't make sense: data.storedArray is never set, so it will never have a value, so you will always end up with a new empty array.
You need to use the same property on the shared object data. For example:
m_FlashCookie.data.storedArray = arr;
m_FlashCookie.flush();
Looking at your code, there's a number of other issues:
The latest score is immediately removed because arr.length < NUM_SAVED_SCORES is going to be true from the start, since arr starts out empty, and it then calls arr.pop() which will remove the latest entry that was just added. So the array is always empty.
It adds the score immediately with arr.push(latest_score_object) instead of waiting until the user clicks save, so the value of the input texts don't matter at all -- the saved values will always be "nama kamu" and "nilai".
The following fixes all the issues mentioned:
var leaderboard = SharedObject.getLocal("leaderboard");
const MAX_SAVED_SCORES:int = 10;
inputBoxName.text = "nama kamu";
inputBoxScore.text = "nilai";
var entries:Array = leaderboard.data.entries || [];
var latestEntry:Object;
displayScores();
btnLoad.addEventListener(MouseEvent.CLICK, loadClick);
btnSave.addEventListener(MouseEvent.CLICK, saveClick);
function loadClick(e:MouseEvent):void {
displayScores();
}
function saveClick(e:MouseEvent):void {
saveLatestScore();
displayScores();
}
function saveLatestScore():void {
// create the latest entry based on input texts
latestEntry = {
name: inputBoxName.text,
score: inputBoxScore.text
};
// add the entry and sort by score, highest to lowest
entries.push(latestEntry);
entries.sortOn("score", Array.NUMERIC | Array.DESCENDING);
// if reached the limit, remove lowest score
if (entries.length > MAX_SAVED_SCORES) {
entries.pop();
}
// store new sorted entries to shared object
leaderboard.data.entries = entries;
leaderboard.flush();
}
function displayScores():void {
var myHTMLL:String = "";
for (var i:int = 0; i < entries.length; ++i) {
// loop through every entry, every entry has a "name" and "score" field as that's what we save.
var entry:Object = entries[i];
// is this the last score that was just entered last gamestate?
if (entry == latestEntry)
myHTMLL += (i+1) + ". <b><font color=\"#0002E5\">"+ entry.name + " " + entry.score +"</font></b><br/>";
else
myHTMLL += (i+1) + ". "+ entry.name + " " + entry.score +"<br/>";
}
myHTML.htmlText = myHTMLL;
}

Resources