Javascript. Is there an easier way to replace text if certain strings are found? - google-apps-script

I'm working with Google Sheets Script Editor and I'm trying to do a check to see if a cell contains the text DROPPED, DENIED, WEBSITE, POTENTIAL, SELECTED, TESTING, INACTIVE, or ACTIVE. If it contains DROPPED or DENIED, I need it to say "DENIED", if it says SELECTED/TESTING/INACTIVE/ACTIVE, it needs to say "ACCEPTED", and if it says WEBSITE/POTENTIAL it needs to say "INCOMPLETE".
My code looks roughly like this. I don't have any formal training so it's probably a mess, but hopefully there's an easier way to do it.
if (('' + data1[i][1]).trim().toUpperCase() == "DROPPED" ||
('' + data1[i][1]).trim().toUpperCase() == "DENIED") {
sh2.getRange(j + 1, 11, 1, 1).setValue("DENIED");
}else if (('' + data1[i][1]).trim().toUpperCase() == "SELECTED" ||
('' + data1[i][1]).trim().toUpperCase() == "TESTING" ||
('' + data1[i][1].trim().toUpperCase() == "INACTIVE" ||
('' + data1[i][1].trim().toUpperCase() == "INACTIVE" ||
('' + data1[i][1].trim().toUpperCase() == "ACTIVE") {
sh2.getRange(j + 1, 11, 1, 1).setValue("ACCEPTED");
}else if (('' + data1[i][1]).trim().toUpperCase() == "WEBSITE" ||
('' + data1[i][1]).trim().toUpperCase() == "POTENTIAL") {
sh2.getRange(j + 1, 11, 1, 1).setValue("INCOMPLETE");
I'm pulling this from a "Cross Check" script that's comparing cell contents between two different sheets, which is why it's using sh2.getRange. Mostly I'm just wondering if there's an easier way to write it than that huge if/elseif. If not, that's fine, it's just going to need to run a couple thousand checks so if I can do it in a way that minimizes the amount of legwork the script does, that would be better.
The only thing I can think of is just forcing the cell to be upper case before it does the checks, and then putting it back to proper case when the checks are done. I don't know if continuing to do toUpperCase is going to take more time or not. Honestly I don't know entirely how that even works, I just know it does. We contracted out the original script and I don't know exactly what he did, he didn't comment anything so I had to figure it out myself.

sh2.getRange(j + 1, 11, 1, 1).setValue(ParseData(data1[i][1]));
function ParseData(data) {
switch (('' + data).trim().toUpperCase()) {
case "SELECTED":
case "TESTING":
case "INACTIVE":
case "ACTIVE":
{
return "ACCEPTED";
}
case "DROPPED":
case "DENIED":
{
return "DENIED";
}
case "WEBSITE":
case "POTENTIAL":
{
return "INCOMPLETE";
}
default:
{
return "";
}
}
}

Create 3 arrays of groupings
Then you can simply check if value is in one of the arrays then print the respective value.
eg: using one array grouping to get the value denied.
let deniedArray = ['DENIED','DROPPED'];
let value = data1[i][1]).trim().toUpperCase();
if( deniedArray.includes(value) ){
sh2.getRange(j + 1, 11, 1, 1).setValue("DENIED");
}

Related

Google Appscript IF Or statement not working

Good day everyone; I am running into an error I can't explain. The scenario is as follows, I have two input boxes that collect information. If no value is entered, I want the if statement to handle it and cause a break. The Input box also has an "x" to close the box, which returns a value of "Cancel". What I am trying to do is capture a condition where if no value is entered OR cancel is passed through, a break will occur. Right now, the problem is Google completely ignores the Or statement. I know individually, my IF logic works, but when coupled with OR it doesn't recognize the condition.
This is my current code:
var propnumber = Browser.inputBox('Enter RFI/RFQ Number', Browser.Buttons.OK);
if(propnumber != "" || propnumber != 'cancel'){} else{
SpreadsheetApp.getActiveSpreadsheet().toast('You must enter a value')
return
};
var myName = Browser.inputBox("Enter the Component Name",Browser.Buttons.OK_CANCEL);
if(myName != 'cancel')
{
I do something
}
As I mentioned in my description, my propnumber condition ignores the or and always accepts the value of cancel or blank. If I remove the or ( || ) then it works with one condition at a time.
I am sure this is something trivial any help appreciated.
What's wrong
The logic in the following part of your code
if(propnumber != "" || propnumber != 'cancel'){
// I assume some code will go here
} else{
SpreadsheetApp.getActiveSpreadsheet().toast('You must enter a value')
return
};
does not match the logic you've described here:
if no value is entered OR cancel is passed through, a break will occur.
Consider the case where propnumber is 'cancel':
propnumber != "" evaluates to true
propnumber != 'cancel' evaluates to false
Therefore the if(... || ...) condition in your code evaluates to true and the (currently empty) if block runs, rather than the else.
How to fix it
Option 1: A literal translation of the logic
if no value is entered OR cancel is passed through, a break will occur
would be
if(propnumber == "" || propnumber == 'cancel') {
SpreadsheetApp.getActiveSpreadsheet().toast('You must enter a value')
return
} else {
// Some action
}
Option 2: If you wish to swap the if and else clauses, you must negate the entire condition. So this will also work:
if(!(propnumber == "" || propnumber == 'cancel')) {
// Some action
} else {
SpreadsheetApp.getActiveSpreadsheet().toast('You must enter a value')
return
}
Note the added parentheses and single negation.
Option 3: use AND instead of OR in your existing code.
The expression !(A || B) is NOT logically equivalent to !A || !B. Instead, it is equivalent to !A && !B (see DeMorgan's Law). So this will also work:
if(propnumber != "" && propnumber != 'cancel') {
// Some action
} else {
SpreadsheetApp.getActiveSpreadsheet().toast('You must enter a value')
return
}

Converting cell coordinates without using a column number to column letter method?

I'm trying to figure out what my options are here when I need to use a column number in a formula, and if I really need to write a column number to column letter method to accomplish what I'm trying to do.
See this method I have here:
createFormulas(lookupField, lookupColumns) {
// Iterate through the lookupColumn array
lookupColumns.forEach(value => {
let columnNumber = this.getColumn(this.headers, value);
let range = this.sheet.getRange(2, columnNumber, this.lastRow - 1, 1);
// range.setFormula('=$A2');
range.setFormula('=' + columnNumber + '2' ); // doesn't work obviously
})
}
I'm trying to add formulas in a column based on the column.
this.getColumn() returns the column number based on the column name being passed in.
let range sets the range I want to set the formula in
range.setFormula('=$A2') pastes this formula into range and updates the reference accordingly (i.e., $A3, $A4, etc.). This isn't the formula I ultimately want to use, just a simplified example.
I need to set the column in the reference dynamically, however.
What I have obviously won't work: range.setFormula('=' + columnNumber + '2' );. That would just result in something like 72 where 7 is the column number.
I know I can write a method that will convert the column number into a letter. I'm just surprised there isn't a built in method for doing that or some other native way of accomplishing this.
For example, in Excel VBA I think you can do something like "=" & Cells(2, columnNumber).Address or something like that (been a while, I could be wrong), which should equate to =A2, =A3, =A4, etc. in the range.
So before writing this column number to letter method, I just wanted to check: is that the only way to accomplish what I'm after or is there a native way of handling this that I'm just not seeing?
Actually, was able to do this using .getA1Notation().
Refactored to the following and it works as expected:
createFormulas(lookupField, lookupColumns) {
// Iterate through the lookupColumn array
lookupColumns.forEach(value => {
let columnNumber = this.getColumn(this.headers, value);
let formulaRange = this.sheet.getRange(2, columnNumber, this.lastRow - 1, 1);
let referenceRange = this.sheet.getRange(2, this.idColumn, this.lastRow - 1, 1);
formulaRange.setFormula("=" + referenceRange.getCell(1, 1).getA1Notation());
})
}
Column To Letters
I followed Yuri's path to the numbers to letter functions and I'm a bit baffled that we have forgotten that there are 26 letters in the alphabet and so after looking at the various functions at that reference none of them seem to have worked for me. So here's my replacement:
function colToletters(num) {
let a = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (num < 27) return a[num % a.length];
if (num > 26) {
num--;
let letters = '';
while (num >= 0) {
letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[num % 26] + letters;
num = Math.floor(num / 26) - 1;
}
return letters;
}
}
This will calculate the column letters for 1 to 1000 and I check all the way to 703 where the letters go to AAA and they look good all the way.
Just in case. Based on https://stackoverflow.com/a/64456745/14265469
function numberToLetters(num) {
// num--; // if you need 1 --> A, 2 --> B, 26 --> Z
let letters = '';
while (num >= 0) {
letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[num % 26] + letters;
num = (num - num % 26) / 26 - 1;
}
return letters;
}
console.log(numberToLetters(0)); // --> A
console.log(numberToLetters(25)); // --> Z
console.log(numberToLetters(26)); // --> AA

Two equals or not two equals

I am working with 5 options: O, R, 1, 2, 3. My function here is trying to convert O and R to numeric equivalents while leaving the number values alone.I have tried this two ways and neither is working. This is the first way I tried it. It does not change any values at all.
function Level2Number (Level) //changes level R and O to numbers
{
switch (Level)
{
case (Level == "R"):
return 0
break;
case (Level == "O"):
return .5
break;
default:
return Level
break;
}
}
When I tried it this next way, it is doing what I want with the O and the R but i is placing the letter O in place of the 1, 2, and 3.
function Level2Number (Level) //changes level R and O to numbers for further calculation.//
{
switch (Level)
{
case (Level = "R"):
return 0
break;
case (Level = "O"):
return .5
break;
default:
return Level
break;
}
}
I am not sure how to go from here.
This doesn't work because logical expressions (as indicated by the parentheses) evaluate to either 'true' of 'false'. Your 'Level' variable is not of type Boolean (neither 'true' nor 'false'), so you'll always be stuck with the default scenario.
Use the following notation
switch(Level) {
case "R":
return 0;
default:
return Level;
}
Btw, putting 'break' after 'return' is completely redundant.

How to make a For Loop of a nested IF Statement?

I am trying to make a for loop for this nested if statement in a Bejeweled clone I am practicing to code. As you can see it's pretty dumb to do it this way, because I want to loop through more matches, so I was wondering if there is a more efficient way than going through and making even more silly If statement nests about 7 times AND for each direction since the grid is 8 by 8.
The if ( i !== 0 ) bit is to prevent null errors. I think with the For statement if it were to be used it can be something like if ( i < loopvar ) instead
Thank you :3
if (i !== 0 && map[i][j].name == map[i-1][j].name) // this nest of if statements are gonna check the one to the left of the jewel we are looping through, and see if they match.
{
map[i][j].jewelsWest++;
if ( i !== 1 && map[i-1][j].name == map[i-2][j].name)
{
map[i][j].jewelsWest++;
}
}
I think recursion might me handy in this case:
map[i][j].jewelsWest = countMe(i, j, -1, 0);
map[i][j].jewelsEast = countMe(i, j, 1, 0);
map[i][j].jewelsNorth = countMe(i, j, 0, -1);
map[i][j].jewelsSouth = countMe(i, j, 0, 1);
private function countMe(x, y, xDiff, yDiff):int
{
if(map[x+xDiff] && map[x+xDiff][y+yDiff] && map[x][y].name == map[x+xDiff][y+yDiff].name)
{
return 1 + countMe(x+xDiff, y+yDiff, xDiff, yDiff);
}
else
{
return 0;
}
}

Checking for same values using if statement in actionscript?

I'm working on a match-3 style puzzle game using Flixel, and so I'm working on checking each row and column to see if there is a match at any given time. However, I have 6 different pieces (as of right now) that are active, and each piece is identified by an integer. Given that, I can check, for each and every single piece, by doing something like this:
public function matchingCheck():void
{
if (piecesArray[0][1] == 1 && piecesArray[1][1] == 1 && piecesArray[2][1] == 1) {
FlxG.log("Yay!");
}
}
However, this is rather unwieldy and would basically cause way too much repetition for my liking.
At the very least, I would like to be able to check if the values in these arrays are equal to one another, without having to specify which value it is. At the very best, I'd love to be able to check an entire row for three (or more) adjacent pieces, but I will settle for doing that part manually.
Thanks for your help!
EDIT: Nevermind, my edit didn't work. It was just checking if piecesArray[2][1] == 1, which makes me a sad panda.
EDIT 2: I've selected the correct answer below - it's not exactly what I used, but it definitely got me started. Thanks Apocalyptic0n3!
You could cut down on that code a little bit by using another function
private function checkValid( arrayOfItemsToCheck:Array, value:* ):Boolean {
for ( var i:Number = 0; i < arrayOfItemsToCheck.length; i++ ) {
if ( arrayOfItemsToCheck[i] != value ) {
return false;
}
}
return true;
}
Then you just do this in your if statement:
if ( checkValid( [ piecesArray[0][1], piecesArray[1][1], piecesArray[2][1] ], 1 ) ) {
FlxG.log("Yay!");
}
That does assume all items need to be equal to 1, though. It's still a lot of code, but it cuts out one set of "= 1 &&" for each check.
How about something like this which would tell you both if a match existed and what match it was:
public function checkForMatch():void{
var rows:int = piecesArray.length;
for(var i:int=0; i<rows; i++){
var match:int = checkRow(piecesArray[i]);
if(match > -1) {
FlxG.log("Yay you matched " + match);
}
}
}
private function ckeckRow(row:Array):int{
if(row[0] == row[1] == row[2]){
return row[0];
}
return -1;
}