Saturday, June 6, 2009

Publishing schedule change

As I mentioned last week, I ran out of code samples that are suitable for publishing. All that remains is code in various states of partial completion. I was trying to avoid this, but have decided I need to change the publishing schedule of this blog from weekly to whenever I have something finished. I have some fairly large projects on the go right now that I need to focus on if I hope to complete them in my own lifetime. I'm not even sure that anyone is ever reading this besides myself as I type it, so I don't expect anyone will be upset by this.

Saturday, May 30, 2009

Dynamic Arrays

I have to shamefully admit... I didn't prepare anything for this week. I'd hate to post nothing, so instead I'll post something which I don't consider completely finished yet, but it's far enough along that it probably works just fine.


Working with arrays in classic ASP is frustrating. I wanted to make it more like working with listbox controls in Visual Basic. Note that I wrote this class before I started learning ASP.NET; I wanted to revamp it to bring it in line with the ArrayList class in VB.NET, but just haven't had time yet. More importantly, I haven't really had reason. All my current projects are in PHP or ASP.NET; I've essentially abandoned classic ASP. But I know there are people out there with classic ASP applications which they can't completely rewrite, so I intend to continue writing classic ASP functions.


Considering the enormous size of this class, there's no way I'm going to attempt posting it here, but it'll be on Snipplr as usual. Count, Item, and Items are implemented as properties. Subroutines and functions include: Remove, RemoveAll, Add, BinarySearch, Exists, Random, Reverse, Sort (calls QuickSort; also available are BubbleSort, CombSort, ExchangeSort, and SelectionSort), Shuffle, Swap, Sum, and Product.


View ASP implementation on Snipplr

Saturday, May 23, 2009

Prime Numbers

Once upon a time I wrote some functions for finding and checking prime numbers. In this library of functions you'll find the following:


  • getPrimes() - finds prime numbers up to a specified limit
  • isPrime() - checks if a number is prime using modular division against odd numbers
  • isPrime2() - checks if a number is prime using modular division against known primes
  • primeCount() - counts the number of primes less than or equal to the specified number
  • isComposite() - the opposite of prime
  • isPrimeSpeedTest() - races isPrime againts isPrime2

In theory, checking against known primes should be faster than checking against all odd numbers, but it turned out to be slower because I was first finding the primes with getPrimes(). If a list of prime numbers was hardcoded in an array it might be faster for smaller numbers, and then the odd number method could be used for larger numbers.


View ASP implementation on Snipplr

Saturday, May 16, 2009

Chemistry Library

This week's code is probably the biggest single release I've ever made. I considered splitting it up into two or three weeks, but that would be too much work. Before we get started, you'll need my proper case function.


In this function library, we have three major things going on: look up chemical element symbols by atomic number, look up chemical element names by atomic number, and figure out the electron configuration of an atom given the atomic number.


For the chemical element names and symbols, I made an array of all the named elements; currently the highest named element is atomic number 111. There are elements with higher atomic numbers that have been discovered, but not yet named. There is also the possibility that more elements may be discovered in the future. To handle both these situations, I wrote code to generate standardized names if the array lookup fails.


The function(s) for figuring out electron configuration are my favorite. I started with an array of all the subshells in the order they get filled. Then I wrote a function that knows how many electrons can fit in each type of subshell. Finally, I wrote the main function which fills the subshells, taking into account known exceptions to the rules. (e.g. palladium)


Saturday, May 9, 2009

Fuel consumption

Last November I was at a Service Canada office and picked up a pamphlet called Fuel Consumption Guide 2007. Although too old to be relevant (unless you're buying a used car), it contained some formulae for calculating fuel consumption which inspired me to write some code.


This library of functions is among the longest yet, so I won't be posting the code directly on the blog, but here's a list of the functions included:


  • lph() - Calculate fuel consumption rating in litres per 100 kilometres.
  • mpg() - Calculate fuel consumption rating in miles per gallon.
  • lph2mpg() - Convert miles per (imperial) gallon to litres per 100 kilometres.
  • mpg2lph() - Convert litres per 100 kilometres to miles per (imperial) gallon.
  • fuelConsumption() - Calculate fuel consumption in litres.
  • CO2emissions() - Calculate carbon dioxide emissions in kilograms.

Saturday, May 2, 2009

Regular Expressions

Over the past year, I've posted a lot of code that made use of regular expressions. When used appropriately, they can be very powerful. But regular expressions in ASP are a little more cumbersome than PHP. Wouldn't it be great if ASP had the simplicity of regular expression functions?


Once again, the source code is too long to post here, so it will only be available on Snipplr. This library of functions includes the following:

  • ereg() - case-sensitive regular expression match
  • eregi() - case-insensitive regular expression match
  • ereg_replace() - case-sensitive regular expression replacement
  • eregi_replace() - case-insensitive regular expression replacement
  • sql_regcase() - make regular expression for case insensitive match

View ASP implementation on Snipplr

Saturday, April 25, 2009

Social Insurance Numbers

Here in Canada, our equivalent of the Social Security Number is called Social Insurance Number. It serves the same purpose and has the same demands for privacy surrounding it.


Once again, I won't be posting the full source code on the blog, only on Snipplr, but I will discuss briefly how it works. It consists of three groups of three digits, and is validated using the Luhn algorithm. I also do a quick regular expression validation to check for numbers which invalidly begin with an eight: ^([1-79]{3})[\-\s]?(\d{3})[\-\s]?(\d{3})$.


Saturday, April 18, 2009

Social Security Numbers

Assuming that you have a legitimate need to capture social security numbers (human resources app?), you may want to validate and format them consistently. The actual code for both languages combined is a little bit too long to post, so I'll just talk about the algorithm.


A social security number is a nine-digit number and can be matched with the following regular expression: ^\d{3}\-?\d{2}\-?\d{4}$. If it can't pass this test, it's not a valid social security number. However, passing this simple test doesn't guarantee validity, so we need to keep checking.



  • No digit group can consist of only zeros, and the first group cannot be 666. We check for these errors with the following regular expression: ((000|666)\-?\d{2}\-?\d{4}|\d{3}\-?00\-?\d{4}|\d{3}\-?\d{2}\-?0000.

  • Numbers from 987-65-4320 to 987-65-4329 are reserved for use in advertisements, and other previously legitimate numbers have been invalidated because of use in advertisments. We check for these errors with the following regular expression: 987\-?65\-?432\d{1}|042\-?10\-?3580|062\-?36\-?0749|078\-?05\-?1120|095\-?07\-?3645|128\-?03\-?6045|135\-?01\-?6629|141\-?18\-?6941|165\-?(16|18|20|22|24)\-?7999|189\-?09\-?2294|212\-?09\-?(7694|9999|219\-?09\-?9999|306\-?30\-?2348|308\-?12\-?5070|468\-?28\-?8779|549\-?24\-?1889)

  • Last but not least, the first three numbers are never higher than 772 (well, not yet; this could change in the future). For this I used a simple string conversion and numeric comparison.


The complete, commented source code is available on Snipplr:


Next week we'll head north of the border to my country and see what the Canadian equivalent of the Social Security Number is, and how we can validate them.

Saturday, April 11, 2009

Homeland Security Advisory System

Back in 2002, a bunch of bureaucrats decided that the United States needed an advisory system so that all federal departments and agencies could communicate what the current threat condition was using the same definitions. Sounds like a good idea to me.


The average citizen can see what the current threat condition is by visiting certain government web sites. The web site for the Department of Homeland Security is a good example.


The Department of Homeland Security also provides a little-known web service which returns the threat condition in XML format so that you can do what you want with it. Let's write a function to retrieve and parse this information down to just the threat condition itself.


ASP



  1. function getThreatLevel()

  2.     dim regEx

  3.     set regEx = new RegExp


  4.     with regEx

  5.         .Pattern = ".*\n.*CONDITION=""(.*)"" />"

  6.         .IgnoreCase = true

  7.         .Global = true

  8.     end with


  9.     dim xmlhttp

  10.     set xmlhttp = Server.CreateObject("Msxml2.ServerXMLHTTP")

  11.     xmlhttp.open "GET", "http://www.dhs.gov/dhspublic/getAdvisoryCondition", "False"

  12.     xmlhttp.send


  13.     getThreatLevel = regEx.replace(xmlhttp.responseText, "$1")


  14.     set xmlhttp = nothing

  15.     set regEx = nothing

  16. end function


As is often the case, the PHP version requires a significantly less amount of code.


PHP



  1. function getThreatLevel()

  2. {

  3.     return eregi_replace('.*CONDITION="(.*)" />', '\1', file_get_contents("http://www.dhs.gov/dhspublic/getAdvisoryCondition"));

  4. }


So now we have a string that contains just the word "ELEVATED" (or whatever the current threat level is at the moment; it's been at elevated for several years at the time of this writing). What can you do with it? Feed it into a switch statement and display an image is one possibility.


Saturday, April 4, 2009

Min/Max

SQL contains a number of aggregate functions that can do things like select the largest or smallest number from a set of values. This could be handy in the programming language itself.


ASP

  1. function max(arrNumbers)
  2.     dim result
  3.     result = arrNumbers(0)
  4.     for each i in arrNumbers
  5.         if i > result then
  6.             result = i
  7.         end if
  8.     next
  9.     max = result
  10. end function
  11. function min(arrNumbers)
  12.     dim result
  13.     result = arrNumbers(0)
  14.     for each i in arrNumbers
  15.         if i < result then
  16.             result = i
  17.         end if
  18.     next
  19.     min = result
  20. end function

View ASP implementation on Snipplr

Saturday, March 28, 2009

getFileType

Last week I showed you how to get the size of a file. This week I'm going to show you how to get the type of a file. This is useful if you want to display an icon or bit of text next to a hyperlink to indicate the file type. Some people like to use this to warn visitors when they link to a PDF file, because PDF readers can take a while to load.


ASP

  1. function getFileType(someFile)
  2.     dim fs
  3.     set fs = Server.CreateObject("Scripting.FileSystemObject")
  4.     getFileType = fs.GetExtensionName(Server.MapPath(someFile))
  5.     set fs = nothing
  6. end function

See you next week!


View ASP implementation on Snipplr

Saturday, March 21, 2009

getFileSize

When you provide a link to a file for the user to download, it's sometimes nice to provide the size of the file so that they know what they're getting themselves into.


ASP

  1. function getFileSize(someFile)
  2.     dim fs
  3.     dim file
  4.     set fs = Server.CreateObject("Scripting.FileSystemObject")
  5.     set file = fs.GetFile(Server.MapPath(someFile))
  6.     getFileSize = FormatFileSize(file.size)
  7.     set file = nothing
  8.     set fs = nothing
  9. end function

The size property returns the size in bytes, which could be a very large number. As you may have guessed, we're going to write a FormatFileSize function to wrap around it.


ASP

  1. function FormatFileSize(size)
  2.     dim units
  3.     dim factor
  4.     units = Array("B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
  5.     factor = log(size) \ 7
  6.     FormatFileSize = Round(size / (1024 ^ factor), 2) & units(factor)
  7. end function

I think it will be a very, very long time before we reach yottabytes, so this function should withstand the test of time. Next week I'll show you how to get the file type and why you might want that.


View ASP implementation on Snipplr

Saturday, March 14, 2009

Soundex

This week we're going to write a function to convert letters to their corresponding soundex codes. But wait, there's more! We'll also allow you to pass in an entire string and convert the whole thing. Here's the set up:


  1. function soundex(someString)
  2.     if len(someString) = 1 then
  3.         ' code to convert a single character
  4.     else
  5.         ' loop through the whole string and convert each character
  6.     end if
  7. end function

Inside the loop, we'll recursively call the soundex function to convert the individual character. Actually, this may not perfectly fit the definition of recursion. The only recursive aspect of it is that the function calls itself, but an entirely different execution path is being followed once inside. But that's good, because we won't have the poor performance that sometimes comes with recursion.


Once again, the full source code is a little too long to post, so you'll have to grab it from Snipplr:


Saturday, March 7, 2009

Luhn Algorithm

This week we're writing a function to verify credit card numbers. Credit cards have a check digit which is generated with the Luhn algorithm. The code is too long to post here, but as always it is posted on Snipplr and linked here for your dissemination.


Saturday, February 28, 2009

Phonetic Alphabet

It seems to me that most people have trouble remembering the phonetic alphabet (probably myself included). Unless you're a pilot or air traffic controller, it's unlikely that your job requires you to have it memorized. People get by with whatever word comes to mind ("A as in Adam"), but this seems like an opportunity to write some code.


For maximum reusability, I'm going to write my function to convert individual characters at a time. Looping through the characters of the string will occur outside the function. The function is too long to paste here in its entirety, but it will be posted on Snipplr and linked at the bottom of this entry.


PHP has key/value arrays where we can specify any keys we want, so we'll declare an array containing all the conversions and use the input to retrieve the correct string from the array. ASP's arrays don't have this flexibility; we could achieve this with a Dictionary scripting object, but I'm concerned about the overhead associated with that. I'm going to use a Select...Case statement (AKA switch statement), but there is a chance that this is actually not any better. I'll leave the determination of this as an exercise to the reader. Also left as an exercise to the reader is the writing of the looping code.


Saturday, February 21, 2009

Add and strip slashes

Good programmers know that they can't trust user-inputted data. We do as much as we can to validate and try to prevent bad data from getting into our applications. But there is even some perfectly valid data that can cause trouble, especially when you're dealing with databases. The single apostrophe is a common occurrence and gotcha for the newbies when they discover the tail end of their string didn't make it into the database.


Our PHP programmer friends have a few different functions at their disposal. Native to PHP are the addslashes() and stripslashes() functions, which basically do what they say. In more common usage these days is the MySQL-native function, mysql_real_escape_string(), which can handle a wider variety of situations. I'm going to go a little bit further and handle the backspace and horizontal tab characters.


The regular expression we'll be using to add slashes is ([\000\010\011\012\015\032\042\047\134\140]), and to remove slashes we'll use \\([\000\010\011\012\015\032\042\047\134\140]). The only difference is the extra pair of slashes at the beginning. These handle, respectively: null, backspace, horizontal tab, new line, carriage return, substitute, double quote, single quote, backslash, and grave accent.


Saturday, February 14, 2009

htmlspecialchars()

If you rigoursly validate your HTML like I do, you've probably seen many times the warning about HTML entities. Inevitably an ampersand makes its way into your code without being properly encoded. It's almost always in a hyperlink where you're trying to pass a QueryString variable or two.


somefile.asp?this=that&blah=meh


That ampersand needs to be encoded as &amp;. Likewise, double quotation marks need to be encoded as &quot; and the greater than and less than symbols need to be encoded as &gt; and &lt; respectively. If you allow users to write things on your web site, then you need a programmatic solution. Our PHP programmer friends have one in their toolbox which we can borrow.


  1. function htmlspecialchars(someString)
  2.     ' Critical that ampersand is converted first, since all entities contain them.
  3.     htmlspecialchars = replace(replace(replace(replace(someString, "&", "&"), ">", ">"), "<", "<"), """", """)
  4. end function
  5. function htmlspecialchars_decode(someString)
  6.     htmlspecialchars_decode = replace(replace(replace(replace(someString, "&", "&"), ">", ">"), "<", "<"), """, """")
  7. end function

View ASP implementation on Snipplr

Saturday, February 7, 2009

Difference between two stardates

As promised last week, I rejigged my code to calculate the difference between two stardates into a proper function and translated it to ASP. I don't have access to an IIS server right now, so I didn't have a chance to test the ASP version yet, but I'm fairly confident that it works. Let me know if it doesn't!

I won't post the source code directly into this post like I usually do, but as usual it will be available on Snipplr. Expect to see this happen more often in the future. However, I'll talk briefly about what's going on under the hood.

The incoming stardates are just strings, and we split them up based on the official format: first two characters to calculate the years, next three characters to calculate the days, and last character to calculate the hours. The order of the stardates is not important - the code checks which is larger. The same magic numbers from last week are used to get the real values, which are then subtracted. After rounding off any decimal places, the three values are formatted into a human-readable string and returned. It should be very straight-forward.

Saturday, January 31, 2009

Stardate

As promised, today was to be a celebration of one year of Reusable Code. That celebration was overshadowed by a catastrophic hard drive failure early this morning, but luckily I managed to perform a successful recovery with Windows Home Server (after much trial and error).

The function I'm going to share this week is my favorite: calculating the stardate for a given Gregorian calendar date. If you're not familiar with the various Star Trek television series, you might not know what a stardate is. It's a fictional method of measuring time, which is supposed to solve the problems of relativistic time dilation caused by travelling at high velocity. In reality, it progressed at the same rate as time here on Earth.

The method of stardate calculation used here is based on Star Trek: the Next Generation, Star Trek: Deep Space Nine, and Star Trek: Voyager. It does not take the Original Series into account. Also, it is calibrated with the airdates of the television series, not with real world dates. That is to say, if you pass in today's date, it does not return the stardate corresponding to today's date in the 21st century, but today's date in the 24th century Star Trek universe. This is intended to be beneficial for roleplaying games.

ASP

  1. function stardate(someDateTime)
  2.     dim season
  3.     dim episode
  4.     dim fractime
  5.     season = cStr(Year(someDateTime) - 1947)
  6.     episode = str_pad(Round(1000 / 366 * DatePart("y", someDateTime), 0), 3, "0", "left")
  7.     fractime = left(cStr((Hour(someDateTime) * 60 + Minute(someDateTime)) / 144), 1)
  8.     stardate = season & episode & "." & fractime
  9. end function

It is important to note here that for the above function, you will also need my str_pad() function.

PHP

  1. function stardate($timestamp)
  2. {
  3.     $season = date("Y", $timestamp) - 1947;
  4.     $episode = str_pad(round(1000 / 366 * (date("z", $timestamp) + 1)), 3, "0", STR_PAD_LEFT);
  5.     $fractime = substr((date("g", $timestamp) * 60 + date("i", $timestamp)) / 144, 0, 1);
  6.     return $season . $episode . "." . $fractime;
  7. }

I'm somewhat ashamed to admit that there are some "magic" numbers at play here. When I originally wrote this function, I had not intended to make it open source, but events over the past year have led me to the realization that if I did not, it would become lost in the sands of time.

Once upon a time I also wrote some code to calculate the difference between two stardates, but it was only written in PHP. I'll have to port it to ASP and perhaps have it ready for next Saturday's posting, but no promises.


Saturday, January 24, 2009

A year of reusable code

In just a few more days, it will be one year since I started this blog. Next week Saturday I plan to mark the occasion by sharing with you, one of my most favorite functions. In the weeks following that, I'll be bringing you some larger functions and function libraries which will hopefully make up for some of the less interesting things I've posted over the past year. Unfortunately I can't promise no more boring code, because eventually the good stuff will run out.


If I'm going to be completely honest, some of the functions I've posted here, I wrote specifically for here. The tag line of this blog purports "Practical examples for real-world progrmaming", and I feel that sometimes I wasn't really living up to that. However, I feel to a certain extent that I lived up to what I resolved not to do, which was post silly examples about animals and cars just to illustrate how code works.


Since this is Saturday, I owe you folks some source code. Since next week's code is going to super awesome, I'm going to shamefully post two quick snippets which might seem lackluster, but these were honestly written out of desire/need. Both are re-creations of PHP functions, requested by a PHP programmer that worked for me and had to code in ASP because that's what the existing code was written in.


First we have the echo() function. Our PHP programmer friends are spoiled with not having to type Response.Write every time they want to put something on the screen. Then again, are we not entitled to the same luxuries? Rather than merely aliasing Response.Write, you can also use \n instead of typing vbCrLf.


  1. sub echo(someText)
  2.     Response.Write Replace(someText, "\n", vbCrLf)
  3. end sub

Our second function for today was written for use with HTML's textarea element and SQL data types that contain text with line breaks.


  1. function nl2br(someText)
  2.     nl2br = Replace(someText, vbCrLf, "<br />")
  3. end function

Saturday, January 17, 2009

Triangular Numbers

A triangular number is the sum of the n natural numbers from 1 to n.


ASP

  1. function triangularNumber(someNumber)
  2.     dim i
  3.     dim result
  4.     result = 0
  5.     for i = 1 to someNumber
  6.         result = result + i
  7.     next
  8.     triangularNumber = result
  9. end function

PHP

  1. function triangularNumber($number)
  2. {
  3.     for($i = 1; $i <= $number; $i++)
  4.     {
  5.         $result += $i;
  6.     }
  7.     return $result;
  8. }

Saturday, January 10, 2009

Array Merge

Did you ever have two arrays that you needed to merge together? If you were programming in PHP, you used the array_merge() function. If you were programming in ASP, you had to write a whole bunch of code because there is no array_merge() function... until now.


ASP

  1. function array_merge(byVal firstArray, byVal secondArray)
  2.     dim totalSize
  3.     dim i
  4.     dim combinedArray
  5.     ' Ensure that we're dealing with arrays.
  6.     if not isArray(firstArray) then
  7.         firstArray = Array(firstArray)
  8.     end if
  9.     if not isArray(secondArray) then
  10.         secondArray = Array(secondArray)
  11.     end if
  12.     ' Set up the new array.
  13.     totalSize = uBound(firstArray) + uBound(secondArray) + 1
  14.     combinedArray = firstArray
  15.     redim preserve combinedArray(totalSize)
  16.     for i = 0 to uBound(secondArray)
  17.         combinedArray(uBound(firstArray) + 1 + i) = secondArray(i)
  18.     next
  19.     array_merge = combinedArray
  20. end function

View ASP implementation on Snipplr

Saturday, January 3, 2009

Perfect Numbers

This week's function is for checking if an integer is a perfect number.


ASP

  1. function isPerfect(someNumber)
  2.     dim i
  3.     dim arrFactors
  4.     arrFactors = Array()
  5.     ' Only positive integers can be perfect.
  6.     if someNumber < 1 then
  7.         isPerfect = false
  8.         exit function
  9.     end if
  10.     ' Calculate the factors for the given number.
  11.     for i = 1 to someNumber
  12.         if someNumber mod i = 0 then
  13.             redim preserve arrFactors(UBound(arrFactors) + 1)
  14.             arrFactors(UBound(arrFactors)) = i
  15.         end if
  16.     next
  17.     ' A perfect number is a number that is half the sum of all of its positive divisors (including itself).
  18.     if someNumber = eval(join(arrFactors, " + ")) / 2 then
  19.         isPerfect = true
  20.     else
  21.         isPerfect = false
  22.     end if
  23. end function

PHP

  1. function isPerfect($number)
  2. {
  3.     // Only positive integers can be perfect.
  4.     if ($number < 1)
  5.     {
  6.         return false;
  7.     }
  8.     // Calculate the factors for the given number.
  9.     for($i = 1; $i <= $number; $i++)
  10.     {
  11.         if ($number % $i == 0)
  12.         {
  13.             $arrFactors[] = $i;
  14.         }
  15.     }
  16.     // A perfect number is a number that is half the sum of all of its positive divisors (including itself).
  17.     return ($number == array_sum($arrFactors) / 2) ? true : false;
  18. }