Understanding Arrays and Structures
in ColdFusion
Part 3 of an Ongoing Saga:
(if you have not yet read parts 1
and 2 of this tutorial, I
strongly urge you to do so prior to reading this final chapter).
If you?ve made it this far, congratulations! You know what an array is, and you
know what a structure is. You can create a 1d or 2d array, and you can create a
structure and nested structures.
This final chapter will show you how to actually use arrays and structures?and
how to use the two together in order to overcome the limitations of each.
If you?ve been with us through parts 1 and 2 of this tutorial, you remember that
we were using the players on a baseball team to illustrate arrays and
structures.
The array looked like (shortened for brevity):
<cfset arrPlayers =
arrayNew(2) />
<cfset arrPlayers[1][1] = ?John?
/>
<cfset arrPlayers[1][2] = ?First Base?
/>
<cfset arrPlayers[1][3] = 30
/>
<cfset arrPlayers[2][1] = ?Bob?
/>
<cfset arrPlayers[2][2] = ?Second Base?
/>
<cfset arrPlayers[2][3] = 32
/>
The problem was accessing the age of the second baseman required somewhat
cryptic code:
<cfoutput>#arrPlayers[2][3]#</cfoutput>
We then delved into structures. A structure holding player info looked like:
<cfset stPlayer = structNew()
/>
<cfset stPlayer.position = ?First Base?
/>
<cfset stPlayer .age = 30
/>
<cfset stPlayer.salary = 200,000
/>
<cfset stPlayer.bats = ?right?
/>
This allowed us to use code that was easy to understand in outputting values:
<cfoutput>#stPlayer.age#</cfoutput>
Very intuitive?but we can only create one structure per player. How can we
create one single variable that will hold information about all members of the
team?
An array of structures.
We will create a 1 dimensional array, called arrPlayers. We did this waaaay back
in the first part of this tutorial, so it should be cake for you now.
<cfset arrPlayers = arrayNew(1)
/>
In the first array position, we want to store all of the information about our
first baseman, John. As we saw in the previous chapter, the best way to do this
is with a structure. So in position 1, we create a structure:
<cfset arrPlayers[1] = structNew()
/>
And to populate that structure:
<cfset arrPlayers[1].playerName =
?John? />
<cfset arrPlayers[1].position = ?First Base?
/>
<cfset arrPlayers[1].age = ?30?
/>
Before we go any further, let?s take a look at what we?ve just created:
You can see the array (indicated in a <cfdump> by a
green table), which contains a structure (indicated in a <cfdump>
by a blue table).
So?<cfoutput>#arrPlayers[1].playerName#</cfoutput>
gives us an output of John. Again, much more straightforward than storing
all of the information in an array. Plus, we can now add more players:
<cfset arrPlayers[2]
= structNew() />
// creates an empty struct
<cfset arrPlayers[2].playerName
= ?Bob? />
// populates the player?s name
<cfset arrPlayers[2].position
= ?Second Base? />
// populates the player?s position
<cfset arrPlayers[2].age
= ?32? />
// populates the player?s age
We?ve now created a second position in our 1 dimensional array. We populated it
with a new structure, and then populated the structure with the relevant
information. We simply continue to do this for each player on the team, and
ultimately we end up with:
This variable, even though it is a series of nested complex variables, is still
one single variable. Furthermore, we can easily get at the information we want.
<cfoutput>#arrPlayers[4].age#</cfoutput>
is clearly going to return 26.
In fact, let?s output all of the player information. An array without a loop is
like a car without an engine. You?ll find that as you use arrays more and more,
you?ll most frequently be looping over them.
1. <cfoutput>
2. <table border="1">
3. <tr>
4. <td style="font-weight:bold;">Player
Name </td>
5. <td style="font-weight:bold;">Position </td>
6. <td style="font-weight:bold;">Age</td>
7. </tr>
8. <cfloop from="1" to="#arrayLen(arrPlayers)#"
index="i">
9. <tr>
10. <td>#arrPlayers[i].playerName#</td>
11. <td>#arrPlayers[i].position#</td>
12. <td style="text-align:right;">#arrPlayers[i].age#</td>
13. </tr>
14. </cfloop>
15. </table>
16. </cfoutput>
Obligatory line-by-line breakdown:
1. Open a <cfoutput> tag. I frequently see people
put their <cfoutput></cfoutput> inside of a
<cfloop>. It?s much better to place the
<cfoutput>?s outside of the loop. This way the tag
is only called once, rather than once for each iteration of the loop.
2. Create an HTML table
3. Create a new table row
4. A column heading (Player)
5. A column heading (Position)
6. A column heading (Age)
7. Ending a table row
8. Our loop. We use the ColdFusion function arrayLen() to determine the number
of times we will loop. In this instance, #arrayLen(arrPlayers)# will evaluate to
9, as we have 9 array positions.
9. Create a new table row for each iteration of the loop.
10. Output each player?s name in a table cell. #arrPlayers[i].playerName# will
accomplish this. Bear in mind that the value of i will increment with each
iteration of the loop.
11. Output each player?s position in a table cell.
12. Output each player?s age in a table cell.
13. Close the table row for each iteration of the loop.
14. End loop
15. Close table
16. Close <cfoutput>
And when we view this in a browser, we see:
Another example of a practical use of arrays and structures is a shopping cart.
This is not meant to be a full-fledged shopping cart tutorial, so I will be
explaining just enough to demonstrate the array/structure aspect.
Assume a session variable called session.shoppingCart. session.shoppingCart is
initialized as a new array (this would be done within your Application.cfm
template). If you?re not familiar with session variables at this point, that?s
ok. For the purposes of this demonstration, simply realize that a session
variable is persistent?it can be accessed from any page within an application.
Each client will start off with their own instance of session.shoppingCart?an
empty array.
<cfdump var=?#session.shoppingCart#?>
Assume each product has a product ID, a color, a size, and a price.
For each item that the client places into the cart:
1. <cfset temp = arrayAppend(session.shoppingCart,
structNew())>
2. <cfset
session.shoppingcart[arrayLen(session.shoppingcart)].productID =
52>
3. <cfset
session.shoppingcart[arrayLen(session.shoppingcart)].color =
"red">
4. <cfset
session.shoppingcart[arrayLen(session.shoppingcart)].size =
"XXL">
5. <cfset
session.shoppingcart[arrayLen(session.shoppingcart)].quantity =
2>
1. First, we create a new structure to hold the information for the item that
the user is placing in the cart. We do this with an arrayAppend()
function. arrayAppend() will add a new position at
the end of a given array. It returns TRUE or FALSE, which is why we use a temp
variable.
If we have 3 items in the cart, arrayAppend(session.shoppingCart, structNew())
will create a 4th array position and populate it with an empty structure.
2. Because we don?t necessarily know what position we?re going to be inserting
the new item into, we do not reference array positions using
session.shoppingcart[4]. Instead, we use the arrayLen() function to create a
more dynamic method. arrayLen() returns the length of the array?which will
always point to the last position (which is where we?re inserting the new item
into the cart). In lines 2 ? 5, we populate the structure that we just created
in the last array position with productID, color, size, and quantity.
<cfdump var=?#session.shoppingCart#?>
Now, let?s say the user adds another item. We would re-use the same code,
inserting the appropriate values. Assume the user has 3 products in the cart.
<cfdump var=?#session.shoppingCart#?>
Want to know how many items are in the cart at any given time? Simply loop over
the array, and add the quantity values together:
<cfset totalItems = 0>
<cfloop from="1" to="#arrayLen(session.shoppingcart)#"
index="i">
<cfset totalItems = variables.totalItems + session.shoppingcart[i].quantity>
</cfloop>
You have: <cfoutput><strong>#variables.totalItems#</strong></cfoutput>
items in your cart
Let?s say the customer decides that green isn?t his (or her) color. They want to
remove the 2nd item from their cart.
<cfset temp = arrayDeleteAt(session.shoppingcart,
2)>
Because the ColdFusion function arrayDeleteAt() returns TRUE or FALSE, we must
use a temp variable. Beyond that, the function requires 2 arguments. The array
to be manipulated, and the position to remove.
Running the arrayDeleteAt() function leaves us with:
What is important to note here is that by removing position 2, everything after
position 2 shifts up. What was an array with a length of 3 is now an array with
a length of 2.
Some languages would have left the array with 3 positions, and a null value at
position 2. Because ColdFusion arrays are dynamic in nature, they will shrink to
accommodate the removal of an element.
Final Thoughts
I urge you to familiarize yourself with ColdFusion?s built in array functions
and structure functions. If you installed the ColdFusion documentation during
your CF install, everything you need is on your server (by default, located at
http://localhost/cfdocs/).
I realize that there are likely still quite a few questions. Arrays and
structures, while not really difficult subjects, do represent the ?next level?
of understanding not only ColdFusion, but programming concepts in general. I?ve
tried to keep these tutorials as simple as possible?yet as comprehensive as
possible, without turning them into advanced level tutorials.
I don?t expect you to feel that you?re ready to sit down and write a shopping
cart application in the next 20 minutes. I *do* expect that you have a
general understanding of an array and a structure?and how to use both
(individually and together). I expect that you will build on this foundation by
experimenting with your own code, as well as
posting questions to the forums, or
e-mailing me when you?re stuck.
If there are lingering questions?ask. If you feel another tutorial (perhaps more
intermediate to advanced level) is warranted?suggest it.
I look forward to hearing from you.
CJ