Saturday, September 27, 2008

Format a number in scientific notation

Surprisingly, neither ASP nor PHP seem to have a built-in way of formatting a number in scientific notation. Today we're going to change that.


ASP

  1. function formatScientific(someFloat)
  2.     dim power
  3.     power = (someFloat mod 10) - 1
  4.     formatScientific = cStr(cDbl(someFloat) / 10^power) & "e" & cStr(power)
  5. end function

PHP

  1. function formatScientific($someFloat)
  2. {
  3.     $power = ($someFloat % 10) - 1;
  4.     return ($someFloat / pow(10, $power)) . "e" . $power;
  5. }

Sunday, September 21, 2008

Logarithms

ASP's log() function returns the natural logarithm of a number. But what if we want a different base? The same problem exists with handheld calculators, and the same trick we use to get around it there can be used here too.


ASP

  1. function logx(number, base)
  2.     logx = log(number) / log(base)
  3. end function

View ASP implementation on Snipplr

Saturday, September 13, 2008

str_repeat

This week we're duplicating another string function from PHP: str_repeat().


ASP

  1. function str_repeat(input, multiplier)
  2.     dim output
  3.     output = ""
  4.     for i = 1 to multiplier
  5.         output = output & input
  6.     next
  7.     str_repeat = output
  8. end function

View ASP implementation on Snipplr

Saturday, September 6, 2008

str_pad

PHP has a very useful function called str_pad(). It allows you to ensure that a string will conform to a specific length by adding characters of your choice on the end of your choice. A good practical example is when you want a number with leading zeros. ASP doesn't have an equivalent function out of the box, but we're going to write one today; as usual, we'll follow the same syntax to make it easy for those who are already familiar with PHP.


ASP

  1. Const STR_PAD_LEFT = "LEFT"
  2. Const STR_PAD_RIGHT = "RIGHT"
  3. Const STR_PAD_BOTH = "BOTH"
  4. ' Pad a string to a certain length with another string.
  5. function str_pad(input, pad_length, pad_string, pad_type)
  6.     dim output
  7.     dim difference
  8.     output = ""
  9.     difference = pad_length - len(input)
  10.     if difference > 0 then
  11.         select case ucase(pad_type)
  12.         case "LEFT"
  13.             for i = 1 to difference step len(pad_string)
  14.                 output = output & pad_string
  15.             next
  16.             output = right(output & input, pad_length)
  17.         case "BOTH"
  18.             output = input
  19.             for i = 1 to difference step len(pad_string) * 2
  20.                 output = pad_string & output & pad_string
  21.             next
  22.             ' Not sure if it will step far enough when difference is an odd number, so this next block is just in case.
  23.             if len(output) < pad_length then
  24.                 output = output & pad_string
  25.             end if
  26.             output = left(output, pad_length)
  27.         case else
  28.             for i = 1 to difference step len(pad_string)
  29.                 output = output & pad_string
  30.             next
  31.             output = left(input & output, pad_length)
  32.         end select
  33.     else
  34.         output = input
  35.     end if
  36.     str_pad = output
  37. end function

Thinking back to my days as a web developer, this was probably the most reused function out of all I'd ever written.


View ASP implementation on Snipplr

Saturday, August 30, 2008

Roman Numerals, Part 3

Back in March, we wrote a function to turn an arabic number into a Roman numeral. That function had a limitation of numbers smaller than 5000. There are ways of writing Roman numerals larger than 5000, but they are not as well accepted by purists because they evolved during later time periods. Out of respect for the purists, this new function will hinge on the old one, rather than replace it.

In the system we will be using, a bar is placed over the numeral to indicate that it is multipled by 1000:

  • V = 5000
  • X = 10,000
  • L = 50,000
  • C = 100,000
  • D = 500,000
  • M = 1 million

Since there are no Unicode characters for this purpose, we will have to cheat a little bit by using some HTML and CSS.


ASP

  1. function bigroman(ByVal arabic)
  2.     dim thousands
  3.     thousands = Array("", "M", "MM", "MMM", "M(V)", "(V)", "(V)M", "(V)MM", "(V)MMM", "M(X)")
  4.     if arabic >= 10000 then
  5.         bigroman = "(" & roman((arabic - (arabic mod 10000)) / 1000) & ")"
  6.         arabic = arabic mod 10000
  7.     end if
  8.     bigroman = bigroman & thousands((arabic - (arabic mod 1000)) / 1000)
  9.     arabic = arabic mod 1000
  10.     bigroman = bigroman & roman(arabic)
  11.     ' Convert parentheses to <span> tags.
  12.     bigroman = replace(bigroman, "(", "<span style=""text-decoration: overline"">")
  13.     bigroman = replace(bigroman, ")", "</span>")
  14. end function

PHP

  1. function bigroman($arabic)
  2. {
  3.     $thousands = Array("", "M", "MM", "MMM", "M(V)", "(V)", "(V)M", "(V)MM", "(V)MMM", "M(X)");
  4.     if ($arabic >= 10000)
  5.     {
  6.         $bigroman = "(" . roman(($arabic - fmod($arabic, 10000)) / 1000) . ")";
  7.         $arabic = fmod($arabic, 10000);
  8.     }
  9.     $bigroman .= $thousands[($arabic - fmod($arabic, 1000)) / 1000];
  10.     $arabic = fmod($arabic, 1000);
  11.     $bigroman .= roman($arabic);
  12.     // Convert parentheses to <span> tags.
  13.     $bigroman = str_replace("(", "<span style=""text-decoration: overline"">", $bigroman);
  14.     $bigroman = str_replace(")", "</span>", $bigroman);
  15.     return $bigroman;
  16. }

Saturday, August 23, 2008

isValidPostCode

In this final installment of the postal code trilogy, we turn our attention to the United Kingdom. Postal codes in the UK are called postcodes. They are similar to postal codes in Canada in that they contain both letters and numbers, but unlike Canadian postal codes, they are variable in length.


A postcode can have any of the following formats:

  • A9 9AA
  • A99 9AA
  • A9A 9AA
  • AA9 9AA
  • AA99 9AA
  • AA9A 9AA

To match all of these formats, we'll use the following regular expression: [A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]|[A-HK-Y][0-9]([0-9]|[ABEHMNPRV-Y]))|[0-9][A-HJKS-UW])\ [0-9][ABD-HJLNP-UW-Z]{2}. You may notice that, like Canadian postal codes, certain letters are only allowed in certain positions, or not at all.


There are also a few special cases that are valid postcodes but deviate from the regular format:

  • Girobank - (GIR\ 0AA)
  • Father Christmas - (SAN\ TA1)
  • British Forces Post Office - (BFPO\ (C\/O\ )?[0-9]{1,4})
  • Overseas territories - ((ASCN|BBND|[BFS]IQQ|PCRN|STHL|TDCU|TKCA)\ 1ZZ)

ASP

  1. function isValidPostCode(postCode)
  2.     dim regEx
  3.     set regEx = new RegExp
  4.     with regEx
  5.         .IgnoreCase = true
  6.         .Global = true
  7.         .Pattern = "^([A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]|[A-HK-Y][0-9]([0-9]|[ABEHMNPRV-Y]))|[0-9][A-HJKS-UW])\ [0-9][ABD-HJLNP-UW-Z]{2}|(GIR\ 0AA)|(SAN\ TA1)|(BFPO\ (C\/O\ )?[0-9]{1,4})|((ASCN|BBND|[BFS]IQQ|PCRN|STHL|TDCU|TKCA)\ 1ZZ))$"
  8.     end with
  9.     if regEx.Test(trim(CStr(postCode))) then
  10.         isValidPostCode = true
  11.     else
  12.         isValidPostCode = false
  13.     end if
  14.     set regEx = nothing
  15. end function

PHP

  1. function isValidPostCode($postCode)
  2. {
  3.     $pattern = "/^([A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]|[A-HK-Y][0-9]([0-9]|[ABEHMNPRV-Y]))|[0-9][A-HJKS-UW])\ [0-9][ABD-HJLNP-UW-Z]{2}|(GIR\ 0AA)|(SAN\ TA1)|(BFPO\ (C\/O\ )?[0-9]{1,4})|((ASCN|BBND|[BFS]IQQ|PCRN|STHL|TDCU|TKCA)\ 1ZZ))$/i";
  4.     return (preg_match($pattern, trim($postCode)) > 0) ? true : false;
  5. }

Saturday, August 16, 2008

isValidPostalCode

This week we're turning our attention to my own country, Canada, and writing a function to validate postal codes. Unlike a ZIP code, a postal code contains letters too; the format is A1A 1A1. The simplest regular expression to validate this would be:

^[A-Z]{1}[\d]{1}[A-Z]{1}[ ]?[\d]{1}[A-Z]{1}[\d]{1}$

But not all letters are used, and some letters can only appear in certain positions. Taking this into account gives us a slightly more complex pattern of:

^[ABCEGHJ-NPRSTVXY]{1}[0-9]{1}[ABCEGHJ-NPRSTV-Z]{1}[ ]?[0-9]{1}[ABCEGHJ-NPRSTV-Z]{1}[0-9]{1}$


But I want to take this a step further and check the validity of the combination of the first three characters. The postal code C2B 4S3 has a valid format, but the code itself is not valid because there is no C2B postal area. I also want to check the postal code against the province to ensure that they match.


The full source code is too long to post here, but is available in its entirety from Snipplr in the language of your choice:


Alternatively, if you wanted the extended validation, but didn't care about the province matching, you could combine everything into one gigantic regular expression:


^(A(0[ABCEGHJ-NPR]|1[ABCEGHK-NSV-Y]|2[ABHNV]|5[A]|8[A])|B(0[CEHJ-NPRSTVW]|1[ABCEGHJ-NPRSTV-Y]|2[ABCEGHJNRSTV-Z]|3[ABEGHJ-NPRSTVZ]|4[ABCEGHNPRV]|5[A]|6[L]|9[A])|C(0[AB]|1[ABCEN])|E(1[ABCEGHJNVWX]|2[AEGHJ-NPRSV]|3[ABCELNVYZ]|4[ABCEGHJ-NPRSTV-Z]|5[ABCEGHJ-NPRSTV]|6[ABCEGHJKL]|7[ABCEGHJ-NP]|8[ABCEGJ-NPRST]|9[ABCEGH])|G(0[ACEGHJ-NPRSTV-Z]|1[ABCEGHJ-NPRSTV-Y]|2[ABCEGJ-N]|3[ABCEGHJ-NZ]|4[ARSTVWXZ]|5[ABCHJLMNRTVXYZ]|6[ABCEGHJKLPRSTVWXZ]|7[ABGHJKNPSTXYZ]|8[ABCEGHJ-NPTVWYZ]|9[ABCHNPRTX])|H(0[HM]|1[ABCEGHJ-NPRSTV-Z]|2[ABCEGHJ-NPRSTV-Z]|3[ABCEGHJ-NPRSTV-Z]|4[ABCEGHJ-NPRSTV-Z]|5[AB]|7[ABCEGHJ-NPRSTV-Y]|8[NPRSTYZ]|9[ABCEGHJKPRSWX])|J(0[ABCEGHJ-NPRSTV-Z]|1[ACEGHJ-NRSTXZ]|2[ABCEGHJ-NRSTWXY]|3[ABEGHLMNPRTVXYZ]|4[BGHJ-NPRSTV-Z]|5[ABCJ-MRTV-Z]|6[AEJKNRSTVWYXZ]|7[ABCEGHJ-NPRTV-Z]|8[ABCEGHLMNPRTVXYZ]|9[ABEHJLNTVXYZ])|K(0[ABCEGHJ-M]|1[ABCEGHJ-NPRSTV-Z]|2[ABCEGHJ-MPRSTVW]|4[ABCKMPR]|6[AHJKTV]|7[ACGHK-NPRSV]|8[ABHNPRV]|9[AHJKLV])|L(0[[ABCEGHJ-NPRS]]|1[ABCEGHJ-NPRSTV-Z]|2[AEGHJMNPRSTVW]|3[BCKMPRSTVXYZ]|4[ABCEGHJ-NPRSTV-Z]|5[ABCEGHJ-NPRSTVW]|6[ABCEGHJ-MPRSTV-Z]|7[ABCEGJ-NPRST]|8[EGHJ-NPRSTVW]|9[ABCGHK-NPRSTVWYZ])|M(1[BCEGHJ-NPRSTVWX]|2[HJ-NPR]|3[ABCHJ-N]|4[ABCEGHJ-NPRSTV-Y]|5[ABCEGHJ-NPRSTVWX]|6[ABCEGHJ-NPRS]|7[AY]|8[V-Z]|9[ABCLMNPRVW])|N(0[ABCEGHJ-NPR]|1[ACEGHKLMPRST]|2[ABCEGHJ-NPRTVZ]|3[ABCEHLPRSTVWY]|4[BGKLNSTVWXZ]|5[ACHLPRV-Z]|6[ABCEGHJ-NP]|7[AGLMSTVWX]|8[AHMNPRSTV-Y]|9[ABCEGHJKVY])|P(0[ABCEGHJ-NPRSTV-Y]|1[ABCHLP]|2[ABN]|3[ABCEGLNPY]|4[NPR]|5[AEN]|6[ABC]|7[ABCEGJKL]|8[NT]|9[AN])|R(0[ABCEGHJ-M]|1[ABN]|2[CEGHJ-NPRV-Y]|3[ABCEGHJ-NPRSTV-Y]|4[AHJKL]|5[AGH]|6[MW]|7[ABCN]|8[AN]|9[A])|S(0[ACEGHJ-NP]|2[V]|3[N]|4[AHLNPRSTV-Z]|6[HJKVWX]|7[HJ-NPRSTVW]|9[AHVX])|T(0[ABCEGHJ-MPV]|1[ABCGHJ-MPRSV-Y]|2[ABCEGHJ-NPRSTV-Z]|3[ABCEGHJ-NPRZ]|4[ABCEGHJLNPRSTVX]|5[ABCEGHJ-NPRSTV-Z]|6[ABCEGHJ-NPRSTVWX]|7[AENPSVXYZ]|8[ABCEGHLNRSVWX]|9[ACEGHJKMNSVWX])|V(0[ABCEGHJ-NPRSTVWX]|1[ABCEGHJ-NPRSTV-Z]|2[ABCEGHJ-NPRSTV-Z]|3[ABCEGHJ-NRSTV-Y]|4[ABCEGK-NPRSTVWXZ]|5[ABCEGHJ-NPRSTV-Z]|6[ABCEGHJ-NPRSTV-Z]|7[ABCEGHJ-NPRSTV-Y]|8[ABCGJ-NPRSTV-Z]|9[ABCEGHJ-NPRSTV-Z])|X(0[ABCGX]|1[A])|Y(0[AB]|1[A]))[ ]?[0-9]{1}[ABCEGHJ-NPRSTV-Z]{1}[0-9]{1}$