Decomposition of Javascript Class and Function Calls - javascript

I am creating a class to convert an integer to a sentence in a natural language. I've got some basic checks going on to ensure that the number given is between -9999 and 9999. I feel like this works for the most part.
However, once the program reaches "this.convertSentence" - past the try/catch block and error checking, I'm wondering what the best practice is now to decompose the problem into the various function calls it will need to run through to get the job done.
What I'm planning on doing with this.convertSentence is doing some checking for number size, etc...and then sending the number off to separate functions to do more work and having them propagate a sentence to return. I'm not sure if I want a variable just within my class to work with or whether I should be passing a variable around for the sentence to build. Things like this I am wondering about.
/**
* A class for converting an integer to a natrual language sentence in Spanish.
* Accepts integers from -9999 to 9999
*
*/
function NumberToWord () {
this.getSentence = function(number) {
// Check for erroneous input. Accepts only -9999 thru 9999 integers
try
{
if(number === parseInt(number) && number > -10000 && number < 10000) {
return this.convertSentence(number);
}
else {
throw new Error("Argument is not an integer between -9999 and 9999");
}
}
catch(e){
console.log(e.name + " " + e.message);
}
};
this.convertSentence = function(number) {
return "This is where I'll start the logic for the sentence";
};
}
var numberToWord = new NumberToWord();
// Tests
console.log(numberToWord.getSentence(9999));
console.log(numberToWord.getSentence(-9999));
console.log(numberToWord.getSentence(10000));
console.log(numberToWord.getSentence(-10000));
console.log(numberToWord.getSentence(0));
console.log(numberToWord.getSentence(1.1));
console.log(numberToWord.getSentence(-9999.1));
console.log(numberToWord.getSentence(10001));
console.log(numberToWord.getSentence(-10001));
console.log(numberToWord.getSentence(5.5));
console.log(numberToWord.getSentence());

There are a few things I found amiss in your code:
You don't need a class. You simply want to convert a number to a sentence. Use a function.
Why are both getSentence and convertSentence public? Only getSentence should be public.
Since your class will (in all probability) only be instatiated once, use the singleton pattern.
Things I would do:
Because you want to make your code modular, I would use the module pattern.
You can delegate specific tasks to different functions, but keep them in a private namespace.
Here's the code:
Number.prototype.toWord = function () {
return function (lang) {
var number = this.valueOf();
if (parseInt(number) === number) {
if (number < 10000 && number > 10000) {
switch (lang) {
case "es":
return toSpanish(number);
case "en":
default:
return toEnglish(number);
}
} else throw new RangeError("Expected an integer between ±10000.");
} else throw new TypeError("Expected an integer.");
};
function toSpanish(number) {
// convert the number to Spanish
}
function toEnglish(number) {
// convert the number to English
}
}();
Then you can use it like this:
var number = 1337;
alert(number.toWord("es"));
Edit: I wrote a simple function which will do what you want. However it's in English. I don't know Spanish so you'll have to implement that yourself. Here's the demo: http://jsfiddle.net/XKYhx/2/

My thinking would be to check how many parts you are going to have to the sentence and build an array to match with the substrings. for example, in English anyway (I don't speak Spanish!)
as natural language you would say (minus) xxx thousand and xxx
since your number has a max / min of ~10000 / ~-10000,
in pseudocode:
var sign = ""
var wholeparts = new Array()
var mantissaparts = new Array()
if number < 0,
sign = "minus"
number = math.abs(number) // turn the number into a positive number now we have the sign
var whole = math.floor(number) //get whole number
var mantissa = number - whole //get the after decimal part if exists
if whole > 1000
wholeparts.push(math.floor(whole/1000)) //get the thousands part
wholeparts.push(whole - parts[0]*1000) // add the hundreds
else
parts.push(whole)
if mantissa.length > 0
do something similar for the mantissa to the mantissaparts array.
At this point you would have the sentence structure broken down then:
string sentance:
foreach (var part in wholeparts)
stringify and check each number, converting to human words depending on index, ie "seven" or "seventy", add each to the string.
if wholeparts.length > 1 : sentence = sentence + " thousand and"
then if you had a mantissa, sentence = sentence + "point" .. then and the mantissa as natural language.
Best breakdown I can think of would be:
method to convert a number (whole or mantissa) to an array,
method to convert the array to natural language, with a parameter saying if it is whole or mantissa for the different wording that would be used.
method that accepts a number in string form and returns the natural language equivalent
Hope that helps .. was thinking on the fly.

Related

How to create a unique value each time when ever I run the java-script code?

I am using Math.random to create a unique value.
However , it looks like after some days , if i run the same script it produces the same value that created earlier.
Is there any way to create unique value every time when ever i run the script.
Below is my code for the random method.
var RandomNo = function (Min,Max){
return Math.floor(Math.random() * (Max - Min + 1)) + Min;
}
module.exports = RandomNo;
The best way to achieve a unique value is to use Date() as milliseconds. This increasing time representation will never repeat.
Do it this way:
var RamdomNo = new Date().getTime();
Done.
Edit
If you are bound to length restrictions, the solution above won't help you as repetition is predictable using an increasing number the shorter it gets.
Then I'd suggest the following approach:
// turn Integer into String. String length = 36
function dec2string (dec) {
return ('0' + dec.toString(36)).substr(-2);
}
// generate a 20 * 2 characters long random string
function generateId () {
var arr = new Uint8Array(20);
window.crypto.getRandomValues(arr);
// return 5 characters of this string starting from position 8.
// here one can increase the quality of randomness by varying
// the position (currently static 8) by another random number <= 35
return Array.from(arr, this.dec2string).join('').substr(8,5);
}
// Test
console.log(generateId());
This pair of methods generates a 40 characters long random string consisting of letters and digits. Then you pick a sequence of 5 consecutive characters off it.

Novice Javascript query about bad input

I am making a simple tip calculator to help myself learn Javascript. The problem I can't solve is how to compensate for "bad input".
In the code below if the user prefaces the numeric input amount with a dollar sign $, the result is NAN.
function tipAmount(){
var dinner=prompt("How much was dinner?");
result = dinner*.10;
alert("Your tip is " +"$"+result );
}
How do I fix that.
You can try to parse out the numeric value with a regular expression:
var match = dinner.match(/\d+\.?\d*/); // parse with a regular expression
if(!match) { // not able to parse
alert("wrong");
}
var price = +match[0]; // convert to a number
result = price * .10;
The regular expression /\d+\.?\d*/ means: one or more digits, and possibly a dot with other digits following. This means that if e.g. dinner is "$1.23", price will be the number 1.23. The same goes for "$ 1.23" or "1.23 dollar" etc - the number will be parsed out with the pattern defined by the regular expression.
The simplest way would be to parse the input into a float, and see if NaN is returned.
if (isNaN(parseFloat(dinner)))
alert("Bad Input")
Just note that 45.2WWW will return 45.2, and so the above will pass.
If you want to make sure what the user typed in is exactly a number, you could do something like this:
var str = '3.445';
var num = parseFloat(str);
if (isNaN(num) || str.length !== num.toString().length)
alert("Bad Input");
try to parse the input as float or integer depending on your needs:
var dinner = parseFloat(prompt("How much was dinner?"));
or
var dinner = parseInt(prompt("How much was dinner?"));
this functions return 0 whether they unable to parse the input as number
Given your approach of using alerts, the following will work:
function tipAmount() {
var dinner=prompt("How much was dinner?");
//convert "dinner" to a number, stripping out any non numeric data
dinner = Number(dinner.replace(/[^0-9\.]+/g,""));
//any unknown data will convert to 0
if(dinner <= 0) {
alert("Please enter a valid amount");
return false;
}
var result = dinner*.10;
alert("Your tip is " +"$"+result );
return true;
}
Please tip more!
Just check if the value is numeric - Javascript's isNaN:
if (isNaN(dinner)) {
alert('Bad number, bub.');
return;
}
Or, if you want to allow users to type in both - just number or an amount with $ at the beginning, you can check for first char:
if( dinner.charAt(0) == '$' )
{
dinner = dinner.substring(1);
}
This way, whenever user types $, your app will just remove it. If they type a normal number it will calculate the tip for you...

Javascript Convert numbers to different formats or string alternative

UPDATED:
Using javascript or jQuery, how can I convert a number into it's different variations:
eg:
1000000
to...
1,000,000 or 1000K
OR
1000
to...
1,000 or 1K
OR
1934 and 1234
to...
1,934 or -2K (under 2000 but over 1500)
or
1,234 or 1k+ (over 1000 but under 1500)
Can this is done in a function?
Hope this make sense.
C
You can add methods to Number.prototype, so for example:
Number.prototype.addCommas = function () {
var intPart = Math.round(this).toString();
var decimalPart = (this - Math.round(this)).toString();
// Remove the "0." if it exists
if (decimalPart.length > 2) {
decimalPart = decimalPart.substring(2);
} else {
// Otherwise remove it altogether
decimalPart = '';
}
// Work through the digits three at a time
var i = intPart.length - 3;
while (i > 0) {
intPart = intPart.substring(0, i) + ',' + intPart.substring(i);
i = i - 3;
}
return intPart + decimalPart;
};
Now you can call this as var num = 1000; num.addCommas() and it will return "1,000". That's just an example, but you'll find that all the functions create will involve converting the numbers to strings early in the process then processing and returning the strings. (The separating integer and decimal part will probably be particularly useful so you might want to refactor that out into its own method.) Hopefully this is enough to get you started.
Edit: Here's how to do the K thing... this one's a bit simpler:
Number.prototype.k = function () {
// We don't want any thousands processing if the number is less than 1000.
if (this < 1000) {
// edit 2 May 2013: make sure it's a string for consistency
return this.toString();
}
// Round to 100s first so that we can get the decimal point in there
// then divide by 10 for thousands
var thousands = Math.round(this / 100) / 10;
// Now convert it to a string and add the k
return thousands.toString() + 'K';
};
Call this in the same way: var num = 2000; num.k()
Theoretically, yes.
As TimWolla points out, it requires a lot of logic.
Ruby on Rails have a helper for presenting time with words. Have a look at the documentation. The implementation for that code is found on GitHub, and could give you some hint as how to go about implementing this.
I agree with the comment to reduce the complexity by choosing one format.
Hope you find some help in my answer.

Math.random() returns value greater than one?

While playing around with random numbers in JavaScript I discovered a surprising bug, presumably in the V8 JavaScript engine in Google Chrome. Consider:
// Generate a random number [1,5].
var rand5 = function() {
return parseInt(Math.random() * 5) + 1;
};
// Return a sample distribution over MAX times.
var testRand5 = function(dist, max) {
if (!dist) { dist = {}; }
if (!max) { max = 5000000; }
for (var i=0; i<max; i++) {
var r = rand5();
dist[r] = (dist[r] || 0) + 1;
}
return dist;
};
Now when I run testRand5() I get the following results (of course, differing slightly with each run, you might need to set "max" to a higher value to reveal the bug):
var d = testRand5();
d = {
1: 1002797,
2: 998803,
3: 999541,
4: 1000851,
5: 998007,
10: 1 // XXX: Math.random() returned 4.5?!
}
Interestingly, I see comparable results in node.js, leading me to believe it's not specific to Chrome. Sometimes there are different or multiple mystery values (7, 9, etc).
Can anyone explain why I might be getting the results I see? I'm guessing it has something to do with using parseInt (instead of Math.floor()) but I'm still not sure why it could happen.
The edge case occurs when you happen to generate a very small number, expressed with an exponent, like this for example 9.546056389808655e-8.
Combined with parseInt, which interprets the argument as a string, hell breaks loose. And as suggested before me, it can be solved using Math.floor.
Try it yourself with this piece of code:
var test = 9.546056389808655e-8;
console.log(test); // prints 9.546056389808655e-8
console.log(parseInt(test)); // prints 9 - oh noes!
console.log(Math.floor(test)) // prints 0 - this is better
Of course, it's a parseInt() gotcha. It converts its argument to a string first, and that can force scientific notation which will cause parseInt to do something like this:
var x = 0.000000004;
(x).toString(); // => "4e-9"
parseInt(x); // => 4
Silly me...
I would suggest changing your random number function to this:
var rand5 = function() {
return(Math.floor(Math.random() * 5) + 1);
};
This will reliably generate an integer value between 1 and 5 inclusive.
You can see your test function in action here: http://jsfiddle.net/jfriend00/FCzjF/.
In this case, parseInt isn't the best choice because it's going to convert your float to a string which can be a number of different formats (including scientific notation) and then try to parse an integer out of it. Much better to just operate on the float directly with Math.floor().

How to store more than 10 digit number in javascript using the var?

First of all,
What am i doing ?
I have to set the limit of emails in our product in webpage.It's handled with the javascript for validation.It handles upto 8 digit numbers fine. But in our QA team enters the more than 17 digit number in the text box of other email field.It throw the negative message.What can i do ???
My sample code is:
if(form.otherEmails) {
if(validEmailArray.endsWith(',')){
var otherEmailLength = validEmailArray.substring(0,validEmailArray.length-1).split(",");
var setLimitOtherEmail = window.parent.document.getElementById('setLimitOtherEmail').value;
if(setLimitOtherEmail == '-1'){
form.otherEmails.value = otherEmailLength;
}
else if(otherEmailLength.length <= setLimitOtherEmail){
form.otherEmails.value = otherEmailLength;
}
else{
alert("More than "+setLimitOtherEmail+ " " +"Recipient emailIds not allowed in this section.\nIf you want to send it to more recipients, Please create a Bulk Contact Group.");
form.otherEmails.focus();
return false;
}
}
else
form.otherEmails.value = validEmailArray;
}
This is due to the limit being a string, and when a string is being compared to a number (length) the number is coerced into a string, not the other way around.
These are then compared lexicographically - and lexicographically "9" is more (>) than "19".
You need to use parseInt(setLimitOtherEmail, 10) to get the value as a number before comparing them.
Try parsing each of the numbers into Integers before performing any comparison operations on them.
var setLimitOtherEmail = parseInt(window.parent.document.getElementById('setLimitOtherEmail').value);
Other than that are you certain otherEmailLength is actually the number that you want? From the looks of it you are taking the substring of validEmail array and splitting it on "," but it doesn't look like you actually get the length of the array. Try adding .length to the end of the value of otherEmailLength.
var otherEmailLength = validEmailArray.substring(0,validEmailArray.length-1).split(",").length;

Categories