
Working with numbers
This section looks at various aspects of working with numbers in CoffeeScript. All of this functionality comes from JavaScript but is made better by using CoffeeScript.
Converting between bases
JavaScript provides a parseInt()
function that is most commonly used to convert strings to numeric values but it can also be used to convert numbers between bases in the range of base 2 to base 32. This section demonstrates converting numbers to and from base 10.
Let's define several base conversion methods in a utility module that we can use in our applications:
convertBase = (number, fromBase, toBase) -> value = parseInt number, fromBase value.toString toBase convertToBase2 = (number, fromBase = 10) -> convertBase number, fromBase, 2 convertToBase10 = (number, fromBase = 2) -> convertBase number, fromBase, 10 convertToBase16 = (number, fromBase = 10) -> convertBase number, fromBase, 16 module.exports = convertBase: convertBase convertToBase2: convertToBase2 convertToBase10: convertToBase10 convertToBase16: convertToBase16
The basic process to convert from one numeric base to another involves using parseInt()
to get a numeric value for the base we are converting from, and then using that number's toString()
method to return the value at the desired base.
We also created some helper methods to make our API more convenient for our users. The convertToBase2()
, convertToBase10()
, and convertToBase16()
functions use CoffeeScript's default parameter feature to provide sensible defaults for the fromBase
parameter. Helper methods like these should be all about convenience.
We can use our convenient helper methods to convert to base 2, 10, and 16. If we need to convert to any other bases, we have the general purpose convertBase()
method.
Consider the following example:
bcu = require './base_conversion_utils' console.log '153 base 10 to base 2:', (bcu.convertToBase2 153) console.log '10011001 base 2 to base 10:', (bcu.convertToBase10 10011001) console.log '153 base 10 to base 16:', (bcu.convertToBase16 153) console.log '10011001 base 2 to base 16 from base 2:', (bcu.convertToBase16 10011001, 2) console.log '153 base 13 to base 17:', (bcu.convertBase 153, 13, 17)
153 base 10 to base 2: 10011001 10011001 base 2 to base 10: 153 153 base 10 to base 16: 99 10011001 base 2 to base 16 from base 2: 99 153 base 13 to base 17: dg
Generating random numbers
We can generate random numbers by using the JavaScript Math
object. Of course, we can make some great utility functions using CoffeeScript that will make using random numbers more convenient to work with.
Let's define our randomization methods in a utility module that we can use with our applications:
getRandomNumberInRange = (minimum, maximum) -> length = maximum - minimum + 1 randomValue = Math.floor (Math.random() * length) minimum + randomValue getRandomNumber = (maximum) -> getRandomNumberInRange 1, maximum getRandomElementFromCollection = (collection) -> randomIndex = getRandomNumberInRange 0, collection.length - 1 collection[randomIndex] module.exports = getRandomNumber: getRandomNumber getRandomNumberInRange: getRandomNumberInRange getRandomElementFromCollection: getRandomElementFromCollection
We have three useful methods to provide randomness to our applications. We begin with a method that calculates a random number between a minimum and maximum value.
The Math.random()
method is at the heart of our method. Math.random()
returns a decimal number greater than or equal to zero and less than 1. The result of Math.random()
is a decimal value with 16 digits of precision.
We normally want a whole number as our random number, so we use the Math.floor()
method to reduce our fractional value to a whole number.
We then created two other methods that make working with our module more convenient.
The getRandomNumber()
method is a specialized form of our more general getRandomNumberInRange()
method for when the user wants to get a random value between 1 and some number.
The getRandomElementFromCollection()
method takes an array and returns a random element from that array.
Consider the following example:
random = require './random_utils' console.log 'Random number between 1 and 1,000:', (random.getRandomNumber 1000) console.log 'Random number between 10 and 50:', (random.getRandomNumberInRange 10, 50) console.log "Random element from ['Cat', 'Dog', 'Hamster']:", (random.getRandomElementFromCollection ['Cat', 'Dog', 'Hamster'])
Its output will be:
Random number between 1 and 1,000: 93 Random number between 10 and 50: 26 Random element from ['Cat', 'Dog', 'Hamster']: Hamster
Converting between degrees and radians
We commonly need to convert numeric values from one unit of measure to another. This is a great candidate for a utility module. In this section, we will look at creating utility methods to convert angles between degrees, radians, and gradians.
Let's define our conversion routines in a utility module we can use with our applications:
PI = Math.PI DEGREES_IN_RADIAN = 180 / PI RADIANS_IN_GRADIAN = 200 / PI radiansToDegrees = (radians) -> radians * DEGREES_IN_RADIAN radiansToGradians = (radians) -> radians * RADIANS_IN_GRADIAN degreesToRadians = (degrees) -> degrees / DEGREES_IN_RADIAN degreesToGradian = (degrees) -> radians = degreesToRadians degrees radiansToGradians radians gradiansToRadians = (gradians) -> gradians / RADIANS_IN_GRADIAN gradiansToDegrees = (gradians) -> radians = gradiansToRadians gradians radiansToDegrees radians module.exports.angles = degreesToRadians: degreesToRadians degreesToGradian: degreesToGradian radiansToDegrees: radiansToDegrees radiansToGradians: radiansToGradians gradiansToDegrees: gradiansToDegrees gradiansToRadians: gradiansToRadians
Our module begins by defining three constants: PI
, DegreesInRadians
, and RadiansInGradian
. PI
is used to calculate the ratios required to convert between degrees, radians, and gradians. The methods that follow will show you how to perform the conversions.
Notice that at the end of this module, we export our conversion methods to an object named angles
. This allows us to namespace our methods to convert angles. We may want to add additional conversion methods converting temperatures, lengths, weights, speeds, and so on.
The following is a demonstration of our conversion utilities in action:
convUtils = require './conversion_utils' console.log '360 deg:', "#{convUtils.angles.degreesToRadians 360} rad" console.log '360 deg:', "#{convUtils.angles.degreesToGradian 360} grad" console.log '6.28 rad:', "#{convUtils.angles.radiansToDegrees 6.28} deg" console.log '6.28 rad:', "#{convUtils.angles.radiansToGradians 6.28} grad" console.log '400 grad:', "#{convUtils.angles.gradiansToDegrees 400} deg" console.log '400 grad:', "#{convUtils.angles.gradiansToRadians 400} rad"
Its output will be:
360 deg: 6.283185307179586 rad 360 deg: 400 grad 6.28 rad: 359.817495342157 deg 6.28 rad: 399.79721704684107 grad 400 grad: 360 deg 400 grad: 6.283185307179586 rad
Checking a credit card checksum
Validating credit cards might require an expensive call to a payment authorization gateway. Before we make the call for authorization, we should verify that the number is at least a valid credit card number.
We can match formats using regular expressions, but this does not give us the full picture.
Credit card numbers (Visa, MasterCard, American Express, and many others) use a formula to calculate a credit card number's check digit. If the check digit is evenly divisible by 10, the number is at least a possible number. If, on the other hand, the check digit is not evenly divisible by 10, the number is not valid, and we won't have to make our call to the payment authorization service.
Let's implement this process as follows:
reduceNumber = (number) -> value = 0 digits = (Number x for x in number.toString().split '') value += digit for digit in digits if value > 9 return reduceNumber value else return value calculateCheckDigit = (creditCardNumber) -> value = 0 index = 0 digits = (Number x for x in creditCardNumber.split '') for digit in digits.reverse() if index % 2 is 1 value += reduceNumber digit * 2 else value += digit index += 1 return value isValidCreditCardNumber = (cardNumber) -> calculateCheckDigit(cardNumber) % 10 is 0 module.exports = isValidCreditCardNumber: isValidCreditCardNumber
We calculate the credit card number's check digit by adding every even number digit to every odd digit and then multiplying it by 2. If the odd digit is greater than 10, you add the tens and ones place values together (that is, if the odd number is 8, then 2 x 8 = 16 and 1 + 6 = 7).
If the check digit is evenly divisible by 10 with no remainder, the credit card number may actually be a valid credit card and we can proceed with the payment authorization.
For example, the number 4,012,888,888,881,881 would be:
(4 x 2) + 0 + (1 x 2) + 2 + (8 x 2) + 8 + (8 x 2) + 8 + (8 x 2) + 8 + (8 x 2) + 8 + (1 x 2) + 8 + (8 x 2) + 1
This becomes:
8 + 0 + 2 + 2 + 16 + 8 + 16 + 8 + 16 + 8 + 16 + 8 + 2 + 8 + 16 + 1
Now, all of the 16s become 1 + 6 = 7 and our calculation becomes the following:
8 + 0 + 2 + 2 + 7 + 8 + 7 + 8 + 7 + 8 + 7 + 8 + 2 + 8 + 7 + 1
Finally, our check digit is 90, so 4,012,888,888,881,881 could be a valid card number.
We demonstrate this by using the check digit validator as follows:
ccv = require './credit_card_validator' # VALID CARD NUMBERS visa1Sample = '4012888888881881' mc1Sample = '5105105105105100' # INVALID CARD NUMBERS visa2Sample = '4012788888881881' mc2Sample = '5555655555554444' console.log "#{visa1Sample} valid? ", (ccv.isValidCreditCardNumber visa1Sample) console.log "#{mc1Sample} valid? ", (ccv.isValidCreditCardNumber mc1Sample) console.log "#{visa2Sample} valid? ", (ccv.isValidCreditCardNumber visa2Sample) console.log "#{mc2Sample} valid? ", (ccv.isValidCreditCardNumber mc2Sample)
Its output will be:
4012888888881881 valid? true 5105105105105100 valid? true 4012788888881881 valid? false 5555655555554444 valid? False
Tip
Downloading the example code
You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
For more information regarding credit card check digits, see the Wikipedia article on the Luhn or modulus 10 algorithm at http://en.wikipedia.org/wiki/Luhn_algorithm.