CoffeeScript Application Development Cookbook
上QQ阅读APP看书,第一时间看更新

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.

How to do it...

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

How it works...

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)

Its output will be:

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.

How to do it...

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

How it works...

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.

How to do it...

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

How it works...

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.

How to do it...

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

How it works...

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.

There's more...

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.