2 dimensional arrays

October 01, 2008

If you are new to programming, then you might not have discovered 2 dimensional arrays yet. They are a powerful tool and well worth learning. This post is for you!

Ever wondered how to store a table of data in a ColdFusion variable? Well 2-dimensional arrays could provide the answer. For instance how would you put this information into a useable format?

Skywalker Luke Mark Hamill
Solo Han Harrison Ford
Kenobi Obi-Wan Sir Alec Guinness

 

OK, let's look at a simple one dimensional array to get started.

<cfset aCharacters = ArrayNew(1) />
<cfset aCharacters[1] = "Skywalker" />
<cfset aCharacters[2] = "Solo" />
<cfset aCharacters[3] = "Kenobi" />

You can also write it like this if you prefer:

<cfset aCharacters = ArrayNew(1) />
<cfset ArrayAppend( aCharacters, "Skywalker" ) />
<cfset ArrayAppend( aCharacters, "Solo" ) />
<cfset ArrayAppend( aCharacters, "Kenobi" ) />

Looping through the information is easy.

<cfoutput>
  <cfloop from="1" to="#ArrayLen( aCharacters )#" index="row">
  Row #row#: #aCharacters[row][1]#<br />
  </cfloop>
</cfoutput>

So how do we add the other columns? The example above can be thought of as one column with 3 rows in a spreadsheet (it's basically just a list), so we just need to add some more columns. This is now a 2 dimensional array as we know have rows and more than 1 column.

<!--- define a 2-dimensional array --->
<cfset aCharacters = ArrayNew(2) />
<cfset aCharacters[1][1] = "Skywalker" />
<cfset aCharacters[1][2] = "Luke" />
<cfset aCharacters[1][3] = "Mark Hamill" />

<cfset aCharacters[2][1] = "Solo" />
<cfset aCharacters[2][2] = "Hans" />
<cfset aCharacters[2][3] = "Harrison Ford" />

<cfset aCharacters[3][1] = "Kenobi" />
<cfset aCharacters[3][2] = "Obi-Wan" />
<cfset aCharacters[3][3] = "Sir Alec Guinness" />

<cfoutput>
  <cfloop from="1" to="#ArrayLen( aCharacters )#" index="row">
  Row #row#: #aCharacters[row][1]#, #aCharacters[row][2]#, #aCharacters[row][3]#<br />
  </cfloop>
</cfoutput>

Pretty simple, but not very easy to read. We can make it easier to read by setting up some variables for our column headers.

<!--- define a 2-dimensional array --->
<cfset aCharacters = ArrayNew(2) />

<!--- define constants for column names --->
<cfset firstname = 1 />
<cfset lastname = 2 />
<cfset actor = 3 />

<cfset aCharacters[1][lastname] = "Skywalker" />
<cfset aCharacters[1][firstname] = "Luke" />
<cfset aCharacters[1][actor] = "Mark Hamill" />

<cfset aCharacters[2][lastname] = "Solo" />
<cfset aCharacters[2][firstname] = "Hans" />
<cfset aCharacters[2][actor] = "Harrison Ford" />

<cfset aCharacters[3][lastname] = "Kenobi" />
<cfset aCharacters[3][firstname] = "Obi-Wan" />
<cfset aCharacters[3][actor] = "Sir Alec Guinness" />

<cfoutput>
  <cfloop from="1" to="#ArrayLen( aCharacters )#" index="row">
  Row #row#: #aCharacters[row][lastname]#, #aCharacters[row][firstname]#, #aCharacters[row][actor]#<br />
  </cfloop>
</cfoutput>

2 Dimensional arrays are a common practice in all languages, however with ColdFusion you also have alternative ways to store this information. The first is to create a structure and store that in an array, the second is to use a ColdFusion Query.

The structure approach can be done like this:

<!--- define a 1-dimensional array --->
<cfset aCharacters = ArrayNew(1) />
<!--- create a structure to store inside our array --->
<cfset stCharacter = StructNew() />

<!--- assign values to our structure --->
<cfset stCharacter.lastname = "Skywalker" />
<cfset stCharacter.firstname = "Luke" />
<cfset stCharacter.actor = "Mark Hamill" />
<!--- store structure in our array --->
<cfset ArrayAppend( aCharacters, StructCopy( stCharacter ) ) />

<cfset stCharacter.lastname = "Solo" />
<cfset stCharacter.firstname = "Han" />
<cfset stCharacter.actor = "Harrison Ford" />
<cfset ArrayAppend( aCharacters, StructCopy( stCharacter ) />

<cfset stCharacter.lastname = "Kenobi" />
<cfset stCharacter.firstname = "Obi-Wan" />
<cfset stCharacter.actor = "Sir Alec Guinness" />
<cfset ArrayAppend( aCharacters, StructCopy( stCharacter ) ) />

<cfoutput>
  <cfloop from="1" to="#ArrayLen( aCharacters )#" index="row">
  Row #row#: #aCharacters[row].lastname#, #aCharacters[row].firstname#, #aCharacters[row].actor#<br />
  </cfloop>
</cfoutput>

You may be wondering what the StructCopy() function does. Well, if we didn't, then when we add the character structure to our array, we are only actually assigning a reference to the structure, not that actual values. This means that you when you loop through the array, all the values will be the same. By using StructCopy(), we are assigning an independant clone of the structure and values which will not be affected by subsequent changes to our struct.

Here's how you would do it using a ColdFusion query object:

<cfset qCharacters = QueryNew( "lastname,firstname,actor" ) />

<cfset QueryAddRow( qCharacters ) />
<cfset QuerySetCell( qCharacters, "lastname", "Skywalker" ) />
<cfset QuerySetCell( qCharacters, "firstname", "Luke" ) />
<cfset QuerySetCell( qCharacters, "firstname", "Mark Hamill" ) />

<cfset QueryAddRow( qCharacters ) />
<cfset QuerySetCell( qCharacters, "lastname", "Solo" ) />
<cfset QuerySetCell( qCharacters, "firstname", "Hans" ) />
<cfset QuerySetCell( qCharacters, "firstname", "Harrison Ford" ) />

<cfset QueryAddRow( qCharacters ) />
<cfset QuerySetCell( qCharacters, "lastname", "Kenobi" ) />
<cfset QuerySetCell( qCharacters, "firstname", "Obi-Wan" ) />
<cfset QuerySetCell( qCharacters, "firstname", "Sir Alec Guinness" ) />

<cfoutput>
  <cfloop query="qCharacters">
  Row #qCharacters.CurrentRow#: #qCharacters.lastname#, #qCharacters.firstname#, #qCharacters.actor#<br />
  </cfloop>
</cfoutput>

So take your pick. I hope this explains 2 dimensional arrays and how to use them.


4 comments

  1. You could also represent the data as a 1-dimensional array of structures (with each array cell containing a structure of data: aCharacters[1].lastname= 'Skywalker').

    If I know or suspect that I'm going to have to search through the data set, I go with the query object option so I can use SQL to retrieve the record or records I want.

    Comment by Brian Swartzfager – October 01, 2008
  2. Hi Brian, two really good points :) I use the query object a lot as it is also really handy to debug with cfdump. I've also use the nested struct approach but didn't want to confuse people new to arrays by having structs in arrays! However, it does allow for readable code so I'll update my post to include it. Thanks for the comment.

    Comment by John Whish – October 02, 2008
  3. If you're on CF8.0.1 you should have a crack with the implicit struct and array creation stuff.

    <pre>
    <cfscript>
    aCharacter = [
          ["Skywalker","Luke","Mark Hamill"],
          ["Solo","Han","Harrison Ford"],
          ["Kenobi","Obi-Wan","Sir Alec Guinness"]
          ];
    </cfscript>
    </pre>

    or
    <pre>
    <cfscript>
    aCharacter = [
          {lastname="Skywalker",firstname="Luke",actor="Mark Hamill"},
          {lastname="Solo",firstname="Han",actor="Harrison Ford"},
          {lastname="Kenobi",firstname="Obi-Wan",actor="Sir Alec Guinness"}
          ];
    </cfscript>
    </pre>

    Take a look at Ben Nadel's blog

    Comment by Stephen Moretti – October 02, 2008
  4. Hi Stephen, Yeah, the CF8 shorthand stuff is great! Another CF8 feature I like that I didn't mention in the post, is being able to loop through an array:
    <cfloop array="#aCharacters#" index="row">

    For any readers that are interested, I did a quick post a while back on the shorthand style that CF8 developers can use:
    www.aliaspooryorik.com/blog/index.cfm/e/posts.details/post/coldfusion-8-shorthand-8

    Comment by John Whish – October 02, 2008

Leave a comment

If you found this post useful, interesting or just plain wrong, let me know - I like feedback :)

Please note: If you haven't commented before, then your comments will be moderated before they are displayed.