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

Posted December 19, 2006 at 2:52 PM by Ben Nadel

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:

  • <!--- 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.

  • <!--- 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.

  • <!--- 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.



Reader Comments

Dec 19, 2006 at 5:39 PM // reply »
3 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.


Dec 19, 2006 at 6:37 PM // reply »
26 Comments

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>


Dec 19, 2006 at 7:53 PM // reply »
67 Comments

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


Dec 19, 2006 at 8:06 PM // reply »
67 Comments

(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


Dec 19, 2006 at 8:50 PM // reply »
26 Comments

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.


Dec 20, 2006 at 7:37 AM // reply »
11,241 Comments

@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.


Dec 20, 2006 at 11:08 AM // reply »
21 Comments

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


Aug 20, 2008 at 2:50 PM // reply »
3 Comments

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!


Aug 20, 2008 at 3:14 PM // reply »
11,241 Comments

@Karen,

Glad to help :)


May 11, 2010 at 1:45 AM // reply »
19 Comments

Hi .. I like this Methodology Good!

* <!--- 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>

Little Tweaks to set in a select Box like this

<select name="class" id="class" tabindex="1">
<cfloop index="intLetter" from="#Asc('A')#" to="#Asc('F')#" step="1">
<!--- Get character of the given ascii value. --->
<cfset strLetter = Chr( intLetter ) />
<cfoutput>
<option value="#strLetter#">#strLetter#</option>
</cfoutput>
</cfloop>
</select>

Cheers


May 11, 2010 at 7:49 AM // reply »
11,241 Comments

@Gavy,

Heck yeah - CFLoop is awesome.


Oct 14, 2010 at 11:56 AM // reply »
3 Comments

One thing to mention regarding the difference between method 1 and 2.

Method 2 is definitely more straight-forward, but it assumes a Latin alphabet. It will produce problems with any modified Latin alphabet such as French, Spanish and in fact most European languages other than English.

So for those who aim to support multiple languages, method 1 is probably better for those purposes. Of course, if the only target language is English, there's nothing wrong with the 2nd one.

Just throwing it in there! Thanks, Mr. Nadel!


Oct 24, 2010 at 1:44 PM // reply »
11,241 Comments

@Helgi,

That's a good point. I'm not too familiar with how people go about dealing with internationalization (i18n). I think a lot of times, people use "resource packs" to abstract the language being used. Perhaps this kind of a list could be moved to a resource pack.


Feb 22, 2011 at 2:14 PM // reply »
10 Comments

I usually opt for a cfscript and looping solution. This time, since I'll be using this snippet of code in several places throughout my "lightweight" site, I decided to create a hard coded list and put it in the application scope.

Unlike Ben, I feel really confident in my ability to create a comprehensive and accurate list of letters of the alphabet. (Yeah, I'm just teasing you, Ben!)

I can put initalize this in the application.cfc in the onApplicationStart method. I can then forget about it until I need to use a list of letters.

1) It's fast enough.
2) It's not a memory hog.
3) It's reusable.
4) It's easy to read.
5) It's easy to update (if and when we add to or remove from letters of the alphabet).

<cfscript>
APPLICATION.GeneralData = structNew();
APPLICATION.GeneralData.aThroughZ = "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";
</cfscript>

Thanks for the conversation and assistance, Ben (et al).



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 22, 2013 at 12:44 PM
Ask Ben: Query Loop Inside CFScript Tags
In cf10, if you call a function that has: local.result = {}; local.result.msg = ""; local.svc = new query(); local.svc.setSQL("SELECT * FROM..."); local.obj = local.svc.exe ... read »
May 22, 2013 at 12:29 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben: What version of Java are you using? Also, did you test users.id to see what Java reports as the data type? I wonder if it's not a Java primitive data type, but getting returned as something ... read »
May 22, 2013 at 11:47 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Dana, Awesome - so it looks like this bug was fixed in ColdFusion 10. Thanks so much for double-checking that. ... read »
May 22, 2013 at 11:37 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
When I c&p and run on cf10, I get: Selected User IDs: 1,4 User 1 selected: YES - YES User 2 selected: NO - NO User 3 selected: NO - NO User 4 selected: YES - YES User 5 selected: NO - ... read »
May 22, 2013 at 11:27 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Tom, Good thought, but no dice. Both of these still exhibit the same behavior: users.id[ users.currentRow ] users[ "id" ][ users.currentRow ] It's just something whacky happening with ... read »
May 22, 2013 at 11:07 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
Could your problem be that "users.id" is actually an ARRAY, not a single value? Perhaps try it again with "users.id[1]" (I only have CF8 here at work). ... read »
May 22, 2013 at 7:52 AM
Nested Views, Routing, And Deep Linking With AngularJS
Hi, Just a quick thank you. As it happens, for my own purposes, the pending ui-router work being done in native angular is likely the one I'll adopt, but your exploration, code and documentation of ... read »
May 22, 2013 at 4:43 AM
How Do You Use The ColdFusion CFParam Tag?
'<cfparam>' or 'isDefined()and <cfset>' performs the same task.Is there any difference? ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools