Unknown error in google app script formula - javascript

Im trying to create a custom formula using Google App Script for a spreadsheet that given two different variables returns one numeric value.
Eg.
a = true and b = "something" return 50
Here is the code;
function VALOR_KM(vehiculo, cliente) {
var especial = (vehiculo == 'especial') ? true : false;
var valor = 0;
function costo(c) { valor = c };
switch (cliente) {
case 'asegurado':
if (especial) costo(80)
else costo(55);
break;
case 'particular':
if (especial) costo(90)
else costo(66);
break;
case 'AA':
costo(3);
break;
case 'audi':
costo(4);
break;
default:
costo(0);
break;
}
return valor;
};
But when i try to use it in a spreadsheet it gives me the #ERROR! "error analyzing formula" code. And I cant tell why its not working, because if i run the script like JavaScript it works.

Despite the formula being correct, it is not being called properly - which is what causes the error analizying formula error message.
As explained here:
The function separator for each Sheet is dependent on the the country chosen from File> Spreadsheet setting "Locale" - For example if you choose United States then function separator is comma but if you choose Germany then it will be a semicolon. What I have notice that each time you change the Country functions separator are automatically changed.
So essentially you just have to use the appropriate argument separator for your locale (which may be either ; or ,).

See if this works
function VALOR_KM(vehiculo, cliente) {
var valor;
switch (cliente) {
case 'asegurado':
valor = (vehiculo === 'especial') ? 80 : 55;
break;
case 'particular':
valor = (vehiculo === 'especial') ? 90 : 66;
break;
case 'AA':
valor = 3;
break;
case 'audi':
valor = 4;
break;
default:
valor = 0;
break;
}
return valor;
};

Related

Do loop with a switch case in javascript

//I need to add one to the total each time the error name is input. For example if I type "S" in the prompt, then it will add 1 to the total steering and if I type "W", it will add to wiper. The loop should run until i entered a null or zero value and calculate the total errors.
<html>
<head><title>Charge Calculator</title></head>
<body>
<script type="text/javascript">
//Declaring Variables
var day;
var data="";
var steering = 0;
var turbo =0;
var wiper =0;
day = prompt("Enter day: ","");
var BR="<br/>";
do
{
data = prompt("Enter Data: ","");
data = input.nextLine();
switch(data)
{
case 'S':
steering++;
break;
case 'T':
turbo++;
break;
case 'W':
wiper++;
break;
}
}
while(data == "")
document.write("day: " +day +BR); //Display destination name
document.write("Steering issue: " +steering +BR);
document.write("turbo Issue: " +turbo +BR);
document.write("wiper Issue: " +wiper +BR);
</script>
</body>
</html>
There are many things to be improved in your code. Be aware that the write() expression will potentially destroy parts of your html-based page. Find out about DOM manipulation commands instead.
The following snippet demonstrates in a very short way how you could collect your inputs. I used your prompt() method simply to show that it can be done but I would always prefer a simple input field instead.
const counts={s:0,t:0,w:0};
while (++counts[prompt("Please enter the error type code (s,t or w):").toLowerCase()]) {}
console.log("steering: "+counts.s+
"\nturbo: "+counts.t+
"\nwipers: "+counts.w);
Everything happens within the expression that calculates the result for the while condition: the input value is converted to lower case and then a property of the object counts will be incremented. This will only work (= return a "truthy" result) for already initialised properties like s, t or w. For all other cases an increment cannot be calculated, resulting in an "NaN" ("not a number") result. This will then end the while loop.
Seems like recursion could be more appropriate solution here. Though #Cartsten's one looks absolutely ok also.
function count() {
const counts = {
s: 0,
t: 0,
w: 0
};
const checkCounts = () => {
let input = prompt(
'Please enter the error type code (s,t or w):'
).toLowerCase();
if (counts[input] !== undefined) {
++counts[input];
return checkCounts();
}
};
checkCounts();
console.log(
`steering: ${counts.s} \n turbo: ${counts.t} \n wipers: ${counts.w}`
);
}
count();

Why is the rest of my JS code not letting my JQuery run?

apologies for the basic question but I'm just starting to learn JS. I'm trying to build a blackjack game, and I started off in my .js file by creating a constructor function (for the cards) and then I defined one of the methods in that function. Here is the code in my .js file (I've not shown what's inside the actual cardToString function as it's about 70 lines long):
EDIT: Some people have asked I include the entire code, so here it is:
function Card(rank, suit) {
this.rank = rank;
this.suit = suit;
this.toString = cardToString;
this.createNode = cardCreateNode;
}
function cardToString() {
var rank;
var suit;
switch (this.rank) {
case "A" :
rank = "Ace";
break;
case "2" :
rank = "Two";
break;
case "3" :
rank = "Three";
break;
case "4" :
rank = "Four";
break;
case "5" :
rank = "Five";
break;
case "6" :
rank = "Six";
break;
case "7" :
rank = "Seven";
break;
case "8" :
rank = "Eight";
break;
case "9" :
rank = "Nine";
break;
case "10" :
rank = "Ten";
break;
case "J" :
rank = "Jack";
break;
case "Q" :
rank = "Queen";
break;
case "K" :
rank = "King";
break;
default :
rank = null;
break;
}
switch (this.suit) {
case "C" :
suit = "Clubs";
break;
case "D" :
suit = "Diamonds";
break;
case "H" :
suit = "Hearts";
break;
case "S" :
suit = "Spades";
break;
default :
suit = nill;
break;
}
if (rank == null || suit == null)
return "";
else
return rank + " of " suit;
}
$(document).ready(function() {
$("#deal").click(function() {
$("#hit").fadeOut('slow');
});
});
I'm completely confused, because when I put this into the .js file the jQuery doesn't work, however when I comment out everything but the jQuery it works fine. I'm sure it's something basic that I'm just not aware of, but I've searched for a while and can't find an answer. Basically, I have no idea why my first few bits of JS stop the jQuery from working.
EDIT: Someone asked me if I checked the console for errors, and sure enough I do get one:
Uncaught SyntaxError: Unexpected identifier
Next to this it said "jsjquery.js:4" and I think 4 refers to the line of code which was an empty line right at the beginning (I have some comments on the first few lines, then left a line before I starting coding). I removed empty line, now it says the error is on line 80, which is this piece of code:
return rank + " of " suit;
This error goes away when I comment out everything but the jQuery.
This might make everything clear to you guys, but I'm still lost!
Thanks.
EDIT: Ok final edit I promise, haha. For anyone who might come across this question looking for an answer: I made a couple of syntax mistakes, but the real problem was that I didn't define the method cardCreateNode. I didn't realise that not doing so would cause everything else to not run. Thank you to everyone who responded!
default :
suit = nill;
break;
unexpected identifier is syntax error. Obviously you mean null here, not nill.
Since you have a different error based on the update of your post I'll answer your latest issue being that you receive an error on this line:
return rank + " of " suit;
You are missing the concatenation on the other side of " of " so it should look like this:
return rank + " of " + suit;
Also in your switch statement you have this line:
default :
suit = nill;
break;
I think here you mean to set the value to null not nill which would look like this:
default :
suit = null;
break;

passing values into functions in javascript

Not sure why the code below is not working. It should take in a string and convert a G to a C and an A to a T and vice versa. However, it collects the input string but doesn't provide any output i.e. the alert just says "here is your reverse complement DNA"
var dnaSequence = prompt("Enter your DNA sequence here", "");
var newSequence = reverseComplement(dnaSequence);
alert("here is your reverse complemented DNA: " + newSequence);
function reverseComplement(dnaString) {
var reverseC = [];
var dnaArr = dnaString.split('');
for (var i = 0; i < dnaArr.length; i++) {
switch (dnaArr[i]) {
case 'A':
reverseC.push('T');
break;
case 'T':
reverseC.push('A');
break;
case 'C':
reverseC.push('G');
break;
case 'G':
reverseC.push('C');
break;
}
}
// Reverse and rejoin the the string
return reverseC.reverse().join('');
}
It should take in a string and convert a G to a C and an A to a T and vice versa.
Then you don't need the reverse(), because you are pushing in order.
Also, Make sure that you are entering uppercase letters into the prompt.
Else, you can force the uppercase.
This is the code with the two fixes:
function reverseComplement(dnaString) {
var reverseC = [];
var dnaArr = dnaString.toUpperCase().split('');
for (var i = 0; i < dnaArr.length; i++) {
switch (dnaArr[i]) {
case 'A':
reverseC.push('T');
break;
case 'T':
reverseC.push('A');
break;
case 'C':
reverseC.push('G');
break;
case 'G':
reverseC.push('C');
break;
}
}
// Reverse and rejoin the the string
return reverseC.join('');
}
var dnaSequence = prompt("Enter your DNA sequence here", "");
var newSequence = reverseComplement(dnaSequence);
alert("here is your reverse complemented DNA: " + newSequence);
The main lesson you need here is how to test and debug your JavaScript code.
First, get familiar with the JavaScript debugger in your browser. Instead of wondering why your code doesn't work, you can see directly what it is doing. Every modern browser has built-in JavaScript debugging tools; for example here is an introduction to the Chrome DevTools.
Second, when you are testing a function like this, don't use prompt() or alert(). Instead, provide a hard coded input string and use console.log() to display the output in the JavaScript debug console. This way you can run the same test case repeatedly. After you get one test case to work, you can add others.
There are several JavaScript testing frameworks if you want to get fancy, but to start with, simply using a hard coded input and console.log() output plus inspection in the JavaScript debugger is fine.
To make it easy to debug a function when you first write it, add a debugger; statement at the beginning. Then it will stop in the debugger and you can single-step through the code to see which parts of your function actually get executed and what all your variable values are at each step of the way.
For example (since it sounds like you were mistakenly testing with lowercase input), you might do this:
var dnaSequence = 'actg';
var newSequence = reverseComplement(dnaSequence);
console.log(newSequence);
function reverseComplement(dnaString) {
debugger;
var reverseC = [];
var dnaArr = dnaString.split('');
for (var i = 0; i < dnaArr.length; i++) {
switch (dnaArr[i]) {
case 'A':
reverseC.push('T');
break;
case 'T':
reverseC.push('A');
break;
case 'C':
reverseC.push('G');
break;
case 'G':
reverseC.push('C');
break;
}
}
// Reverse and rejoin the the string
return reverseC.reverse().join('');
}
Now, if you have the DevTools open, it will stop in the debugger at the first line of your function. You can single-step through the function to see which of the case statements it actually goes to, and you will see that it doesn't go to any of them. You can also look at the value of dnaArr[i] and see whether it matches any of the case values.

Swap background-color of a specific div with switch/case

I'm trying to swap the background-color of some specific divs basing on their content.
Their background-color should swap when their input is like "Lifestyle" or "Politics" or "Economy" or "Local" or "Sports" or "News".
var colorInput = document.getElementById('views-field-field-section').textContent;
switch (colorInput) {
case 'Lifestyle':
document.getElementById('views-field-field-section').style.backgroundColor = '#9518b8';
break;
case 'Local':
document.getElementById('views-field-field-section').style.backgroundColor = '#009fe3';
break;
case 'Sports':
document.getElementById('views-field-field-section').style.backgroundColor = '#95c11f';
break;
case 'Economy':
document.getElementById('views-field-field-section').style.backgroundColor = '#d40d10';
break;
case: 'Politics':
document.getElementById('views-field-field-section').style.backgroundColor = '#ffcc00';
break;
default:
break;
}
http://jsfiddle.net/gFN6r/501/
You cannot use ids more than once in an html document. This would be invalid html. I have changed the id to a class, and then used the following code and it works:
var colorInput = document.getElementsByClassName('views-field-field-section');
for(i=0; i<colorInput.length; i++) {
var colorInputText = colorInput[i].textContent.trim();
switch (colorInputText) {
case 'Lifestyle':
colorInput[i].style.backgroundColor = '#9518b8';
break;
case 'Local':
colorInput[i].style.backgroundColor = '#009fe3';
break;
case 'Sports':
colorInput[i].style.backgroundColor = '#95c11f';
break;
case 'Economy':
colorInput[i].style.backgroundColor = '#d40d10';
break;
case 'Politics':
colorInput[i].style.backgroundColor = '#ffcc00';
break;
default:
text ='Nix!';
}
}
Here is the jsfiddle:
http://jsfiddle.net/gFN6r/505/
Oh man. Don't use the same id :) But if it is necessary, ok...
I adjusted a little bit your source code, e.g. there was some syntax error, and added jQuery, hope it's not a problem :)
If you use the same id, this will not work - $('#myid'), but this will - $('[id=myid]')
Don't forget to use trim-like function to remove trailing spaces.
And please think a little about how to avoid the same id in your code.
http://jsfiddle.net/gFN6r/506/
$('[id=views-field-field-section]').each(function() {
var text = $(this).text();
text = $.trim(text);
switch (text) {
case 'Lifestyle':
$(this).css({backgroundColor: '#9518b8'});
break;
case 'Local':
$(this).css({backgroundColor: '#009fe3'});
break;
case 'Sports':
$(this).css({backgroundColor: '#95c11f'});
break;
case 'Economy':
$(this).css({backgroundColor: '#d40d10'});
break;
case 'Politics':
$(this).css({backgroundColor: '#ffcc00'});
break;
default:
$(this).text('Nix!');
break;
}
});
Here you have it using jQuery to simplify plunker.
The id is set on the containing parent, while you need to iterate over the children and to check for their content. Note that I've used .trim() to eliminate start and trailing spaces so the match case would catch.
Enumerate use abs 1 -1 =0 -1 = -1(abs) gets you back to 1 so if you start with $case=1 and just -1 as an absolute value you will tick tock between 1 & 0 either way you always subtract 1

Retrieve csv attachment file from gmail and place the data in a google spreadsheet

I currently receive emails from a specific source with multiple attachments specifically csv attachments. What I need is to retrieve the data from the csv attachment. I've looked into creating a Google App Script which I hear can get the job done based on my research but perhaps there is a better way if so please advice.
I wrote a pseudo code of what I would like the script to do:
Determine who sent the email. If its the source that I need then follow to step 2.
Look at the subject of the email if its the subject that I need then proceed to step 3.
If step 1 and 2 are good then the next step is to retrieve the data from one of the csv attachments(based on the name) this is because there could be more than one attachment in the email.
Open the attachment copy the data and paste it in either a google spreadsheet or excel spreadsheet which is created dynamically OR save the attachment to my google drive in a specific folder but either one could work. The trick here is to loop through all the emails in my inbox in past month and achieve the above task.
Thanks everyone for your help and I hope I was clear in my specifications.
Links I found to be helpful to me but not quite exactly what I need.
Create time-based Gmail filters with Google Apps Script
Trigger Google Apps Script by email
After researching and working along with the google apps script documentation I was able achieve my goal for my task at hand. Please see my code below with comments and hopefully this can help.
Thanks,
function RetrieveAttachment() {
// variables being used i, j, k, n, m, a, d, x
var threads = GmailApp.search('*SubjectName*') //search gmail with the given query(partial name using * as a wildcard to find anything in the current subject name).
var msgs = GmailApp.getMessagesForThreads(threads); //retrieve all messages in the specified threads.
//var sheet = SpreadsheetApp.create('test_filename', 2, 8); //creates a new spreadsheet in case I need to create it on a separate file.
//you can get the id from your own google spreadsheet in your browser bar.
var sheet = SpreadsheetApp.openById('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx').getSheetByName('your sheet name');
sheet.clearContents(); //clears all the data in the specified tab, the code below will recreate the dataset once again.
for (var i = 0; i < msgs.length; i++) {
for (var j = 0; j < msgs[i].length; j++) {
var emailDate = msgs[i][j].getDate();
var attachments = msgs[i][j].getAttachments();
for (var k = 0; k < attachments.length; k++) {
/*search for the attachment by name, stringLen returns the start position number of the word 'filename' ignoring any previous characters, counting starts at 0.
e.g. "test_filename", will output the number 6 "test_" will ends at 5 and 6 will start at "f" for filename. Than we use substring to get the actual name out of
attachment name then we use the stringLen variable as a starting position and also as an end position plus the number of characters in word I'm searching for
to get the attachment name, 8 is used because this is how many letters are in the string. Finally we create the stringValue variable and compare to see which
attachments meet the criteria that we are looking for and return only that attachment.*/
var attachmentName = attachments[k].getName();
var stringLen = attachmentName.search('filename');
var stringValue = attachmentName.substring(stringLen,stringLen + 8);
if (stringValue == 'filename') {
var attachmentData = attachments[k].getDataAsString();
var attachmentClean = attachmentData.split('"').join(',');
var attachmentCleanA = attachmentClean.split(',');
/*input headings into the spreadsheet. This is depending on how many columns or fields the attachment file has. The numbers after the "attachmentCleanA" is the
position number of the field you want is located i.e [1][4][7][10]. The reason it skips the numbers is because the getDataAsString() method returned an array with
multiple separations that had to be parsed by quotations. So [2][3] had an empty string value.*/
sheet.getRange('A1').setValue(attachmentCleanA[1]); //Field One
sheet.getRange('B1').setValue(attachmentCleanA[4]); //Field Two
sheet.getRange('C1').setValue(attachmentCleanA[7]); //Field Three
sheet.getRange('D1').setValue(attachmentCleanA[10]); //Field Four
//Extra fields if you want to add.
sheet.getRange('E1').setValue('Email Date'); //Email Date
sheet.getRange('F1').setValue('Email Month'); //Email Month
sheet.getRange('G1').setValue('Email Year'); //Email Year
sheet.getRange('H1').setValue('Source Name'); //Attachment Name
var n = LastRow(sheet); //calls the LastRow function to get the next empty cell.
var m = attachmentCleanA.length + n;
/*attachmentCleanA.length alone is not useful as a limit in the loop because the n variable ends up being bigger than the actual attachmentCleanA.length.
To fix this I added the "attachmentCleanA" + "n" variable so that the n variable will always be less than the attachmentCleanA.length expression.*/
var range = sheet.getRange('A1:H30000'); //this has to match the number of columns in the above sheet.getRange().setValue methods.
var d = 11;
/*now we loop through each string in the array and place it in each individual row and column. The first string position you want may vary depending
on the file you have. The file I have has the first item and is positioned in the 12th position of the array. The reason variable d shows 11 is because
it will be added before the actual extraction of the value "d++" */
RowLoop:
for (var x = n; x < m; x++) {
for (var a = 1; a < 5; a++) {
var cell = range.getCell(x, a);
d++;
//the reason of the if function is so that when I run into an empty string in the array I can simply ignore it and continue to the next string.
if (attachmentCleanA[d] !== "" && attachmentCleanA[d] !== undefined) {
cell.setValue(attachmentCleanA[d]);
}
else if (attachmentCleanA[d] == "") {
/*the a-- is used so that when I find and empty string in the array I don't want to skip to the next column but continue to stay there until I find
a none empty string.*/
a--;
}
}
/*email date - the reason of the if function is because in my situation it was producing more values at the end of the loop. So I made it stop if in
column A doesn't have a value*/
var setDate = range.getCell(x, 5);
if (range.getCell(x, 1).getValue() !== "") {
setDate.setValue(emailDate);
}
else if (range.getCell(x, 1).getValue() == "") {
break RowLoop;
}
//source name
var attachmentLen = attachmentName.search('filename');
var attachmentValue = attachmentName.substring(0, attachmentLen-1);
var setAttachmentName = range.getCell(x, 8);
setAttachmentName.setValue(attachmentValue);
//email year
var setYear = range.getCell(x, 7);
setYear.setValue(emailDate.getFullYear());
//email month
var setMonth = range.getCell(x, 6);
var monthName = MonthFunc(emailDate.getMonth());
setMonth.setValue(monthName);
}
}
}
}
}
}
function LastRow(sheetName) {
//retrieve the last row position after each attachment data file has been put into the spreadsheet
var column = sheetName.getRange('A:A');
var values = column.getValues(); // get all data in one call
var ct = 0;
while ( values[ct][0] !== "" ) {
ct++;
}
return (ct)+1; // add 1 to get the row which is empty
}
function MonthFunc(inputMonth) {
//this function returns the short name of the month.
var monthNumber = inputMonth
switch (monthNumber) {
case 0:
return "Jan";
break;
case 1:
return "Feb";
break;
case 2:
return "Mar";
break;
case 3:
return "Apr";
break;
case 4:
return "May";
break;
case 5:
return "Jun";
break;
case 6:
return "Jul";
break;
case 7:
return "Aug";
break;
case 8:
return "Sep";
break;
case 9:
return "Oct";
break;
case 10:
return "Nov";
break;
case 11:
return "Dec";
break;
}
}

Categories

Resources