Ask Ben: Looping From 'A' To 'Z' In ColdFusion

Posted December 19, 2006 at 2:52 PM

Tags: ColdFusion, Ask Ben

Is there an easy way to list from a to z in the alphabet?

There are few ways to loop over the letters A to Z using ColdFusion. Some are more complicated than others, but perhaps more useful. Let's start off by looking at traditional list loop. If you want, you could create a list of all letters between A and Z and loop over that:

 Launch code in new window » Download code as text file »

  • <!--- Loop over letters as a list. --->
  • <cfloop
  • index="strLetter"
  • list="A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z"
  • delimiters=",">
  •  
  • #strLetter#
  •  
  • </cfloop>

This method is VERY simple and VERY straightforward. But, on the other hand, it is prone to error. Are you sure you wrote down all the letters? Did you miss any?

The next method would be to use an index loop based on the ASCII values of the letters from A to Z. The ASCII values are such that going from A to Z is an incrementing value list.

 Launch code in new window » Download code as text file »

  • <!--- Loop over ascii values. --->
  • <cfloop
  • index="intLetter"
  • from="#Asc( 'A' )#"
  • to="#Asc( 'Z' )#"
  • step="1">
  •  
  • <!--- Get character of the given ascii value. --->
  • <cfset strLetter = Chr( intLetter ) />
  •  
  • #strLetter#
  •  
  • </cfloop>

This reduces room for error since you don't have to write out all of the letters. But on the flip side, you have go through the extra step of converting the ASCII value back to a character value, and it might not be as straight forward to someone reading the code.

Sometimes, what I like to do is load the letters right into a query. I find a query loop more intuitive some reason and it is much more reusable than a standard list.

 Launch code in new window » Download code as text file »

  • <!--- Create a query for the letters. --->
  • <cfset qLetter = QueryNew( "" ) />
  •  
  • <!--- Add the letters (one per record) to the query. --->
  • <cfset QueryAddColumn(
  • qLetter,
  • "name",
  • "CF_SQL_VARCHAR",
  • ListToArray(
  • "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z"
  • )
  • ) />
  •  
  • <!--- Loop over letter query. --->
  • <cfloop query="qLetter">
  •  
  • #qLetter.name#
  •  
  • </cfloop>

This is clearly the most complicated of the three options, and certainly the least straight forward... but, I once you get the letters into the query, they are highly reusable. The physical CFLoop tag is much more simple, and it's more intuitive to scope the letter (which will reduce naming conflicts).

In practice, I tend to go with the ASC() to ASC() value index loop. It is a good combination of clear-to-read code and reduced risk of error.

Download Code Snippet ZIP File

Comments (9)  |  Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page




Reader Comments

Thanks for the the answer. I like number two. I'm not even going to think about three right now. Basics now, advanced later.

Posted by Kit Claudio on Dec 19, 2006 at 5:39 PM


This is where CFSCRIPT is better than a tag. I've had this example in my Complete CFSCRIPT reference (http://www.houseoffusion.com/tutorial/cfscript/index8.cfm) for years and as you can see, the only time you have to do a transformation is on incrementation.

<CFSCRIPT>
For (i="M";i NEQ "P"; i=Chr(Asc(i)+1))
writeoutput(i&' ');
</CFSCRIPT>

Posted by Michael Dinowitz on Dec 19, 2006 at 6:37 PM


Firstly... Ben: your blog is usually quite interesting but today's tutorial is a bit torturous. Manually write out a list only to convert it into an array before converting it again into a query? Jesus. What a shambles. I realise you're just demonstrating... err... *something*... but god what a shite suggestion.

Michael. Hang your head in shame. I normally respect what comes out of your brain, but your example is on a par with Ben's (last) one. You're trying to be clever with your usage of for() expressions, I can see that; but have just come up with rubbish. It's both poor code, and poor logic behind the code in the first place.

You're claiming your own solution only needs "transformation" on increments. Yes. TWO transformations for each iteration. ASC() and CHR(). Applying some sense, one would evaluate the boundaries of the loop ONCE, and then iterate over them, rather than reevaluate them each time (despite knowing the answer of that reevaluation already).

For (i="M";i NEQ "P"; i=Chr(Asc(i)+1))

Get rid of all that sh*t.

iStart = asc('M');
iStop = asc('P');
for (i=iStart; i le iStop; i=i+1)//etc

For one, it's easier to read; for a second: it's better code; a trivial third is that it would be infinitesimally faster (which is not a sensible consideration).

--
Adam

Posted by Adam Cameron on Dec 19, 2006 at 7:53 PM


(and, much as it pains me to advocate CFML over CFscript... doing that in a <cfloop> would be better still, and gets us back to Ben's second suggestion).

--
Adam

Posted by Adam Cameron on Dec 19, 2006 at 8:06 PM


Which is why I gave the link so people can see the example in context. I'm not saying its what should be done, it's just what can be done in CFSCRIPT:
"8.6 The variable set and the end operation do not have to be numeric in nature. This example shows how to set the initial value to a character, increment the character and check it to see if the loop should end. It will output the letters M, N and O seperated by a space."

A CFLOOP can only compare numbers while a for loop can compare any ascii value. That's the bottom line of my original entry and of my post.

Posted by Michael Dinowitz on Dec 19, 2006 at 8:50 PM


@Michael,

I happen to like your solution, and unlike Adam, I do find it readable. It emphasizes the FROM and TO parts of the loop. Frankly, the incremental portion doesn't even need to be as readable if you understand what the loop is doing overall.

And, from what I can see, Adam's loop example is doing pretty much the same as one of my examples, just breaking it out into ??more?? readable steps.

@Adam,

Admittedly, I wouldn't go for option 3 most of the time. But, there are times where having the letters in a query is awesome. Think about taking a query returned from the database and grouping it based on letters? (think last name). Having the whole alphabet in a query would allow you do an easy GROUP BY and find letters where there were no matching records.

On top of that, the query is the most easily reusable. You could loop over it at the top of the page (outputting a list of letters as links or anchors) and then do the same thing on the bottom of the page, and use it to join to another query. It's more complex upfront, but it becomes much more flexible and reusable in the long run.

The best solution is going to depend on the situation.

Posted by Ben Nadel on Dec 20, 2006 at 7:37 AM


Hi All,

For all that previous ways, the best way I like to use is this:

The best solution is going to depend on the situation.

Ameen

Posted by Ameen on Dec 20, 2006 at 11:08 AM


I was trying to find an elegant way to create an A-Z list, and this blog entry came up in a google search.

I ended up using the third solution so that I could loop over the array and compare to records in my database that had data under them, and then used the results to create named anchors for my page.

Thanks Ben!

Posted by Karen Leary on Aug 20, 2008 at 2:50 PM


@Karen,

Glad to help :)

Posted by Ben Nadel on Aug 20, 2008 at 3:14 PM


Post Comment  |  Ask Ben


Home   |   Web Log   |   ColdFusion   |   Projects   |   Resume   |   Job Form   |   Search   |   Contact
Epicenter Consulting - Custom Software Solutions for Business Evolution HostMySite.com - The Leader In ColdFusion Hosting