Introduction to Data Structure in ColdFusion: Structs

David Medlock
David Medlock
Share

Structures are a very useful means of organizing data in your ColdFusion programs. But, they’re often misunderstood, overlooked, or not used to the full — even by the most skilled ColdFusion coders.

In this article, we’ll take a look at what structures are, and how you can use them. We’ll also look at some of the most common structures that many people overlook on a regular basis.

What is a Struct?

First of all, what is a structure (or “struct’, as it’s known in ColdFusion)? A struct is a means of storing data. If you’ve done any programming in Java, you might liken it to a HashMap. It’s also similar to a hash in Perl. We can store a series of related data in a struct. Every piece of data has a name and a value, and we can retrieve a particular value by calling the struct and passing it the name of the value we want. Think of this process as being similar to storing several variables with different names inside one variable.

Let’s start with an example. Imagine we’re building an online book store and we want to store information about a single book in one data structure. Now, normally, we would probably store our data in a database, and use a query to access and display it. But, for this example, we’re going to use structs. Every book is going to have a title, an author, a description, a publish year, and an ISBN number. We’ll start by creating a struct to hold this data.

<cfset myBook = StructNew()>

Here, we create a variable called myBook, and we tell ColdFusion that this is going to be a structure. If we don’t explicitly tell ColdFusion that this will be a structure, it won’t work correctly. Now, let’s add our data to the struct:

<cfset a = StructInsert(myBook, \"title\", \"All About ColdFusion\", 1)>

Let’s stop and talk about this StructInsert function before we move on. The first parameter of the function is the name of the struct to which we want to add a key. The next parameter is the name of the key we want to add. For this parameter, you can use a variable or a string literal (as we’ve done here). The next parameter is the value for the key. Just as with the name of the key, we can use a variable or a string literal in this case. The fourth parameter is an optional parameter — the “allow overwrite” parameter. If this parameter is set to 1, it will overwrite any existing key named “title” in this struct. If it is set to 0, an error will be thrown if you try to add “title” to a struct that already has a key by that name. The default for this parameter is 0. So, let’s add the rest of our data:

<cfset a = StructInsert(myBook, "author", "David Medlock", 1)> 
<cfset a = StructInsert(myBook, "description", "Information about CF", 1)>
<cfset a = StructInsert(myBook, "publishyear", "2004", 1)>
<cfset a = StructInsert(myBook, "ISBN", "ABCD123456", 1)>

Ok, our book contains all the information we need. Now, we want to display our book. This is easy. If you know the names of all the keys for your structure, you can easily display them like this:

<cfoutput> 
 Title: #myBook["title"]#<br />
 Author: #myBook["author"]#<br />
 Description: #myBook["description"]#<br />
 Publish Year: #myBook["publishyear"]#<br />
 ISBN: #myBook["ISBN"]#
</cfoutput>

Here, we simply dump out the contents of the structure, one key at a time. But, what happens if we don’t know what keys a struct contains? It’s no problem — we can use ColdFusion’s loop tag to output all the keys in the struct.

<cfloop collection="#myBook#" item="key"> 
 <cfoutput>
   #key#: #myBook[key]#<br />
 </cfoutput>
</cfloop>

That’s really all we have to do in order to display all the keys in our structure and their values. But, as you’ll notice, this doesn’t output a very pretty label for each item. It just displays the key name. Let’s work on fixing that next.

Storing Structures in Arrays or Other Structures

Just as we can store arrays inside arrays, we can also store structs inside structs and arrays. For example, we can create a struct called titles that will store a user-friendly label for each of the keys in our array. We’ll use the exact same keys we used in our book struct.

<cfset titles = StructNew()>  
 
<cfset a = StructInsert(titles, "title", "Title", 1)>  
<cfset a = StructInsert(titles, "author", "Author", 1)>  
<cfset a = StructInsert(titles, "description", "Book Description", 1)>  
<cfset a = StructInsert(titles, "publishyear", "Publish Year", 1)>  
<cfset a = StructInsert(titles, "isbn", "ISBN Number", 1)>

That was easy. Now, we’ll get a little more complex with our data structures. We’re going to create a struct called bookshelf and put myBook and titles into it.

<cfset bookshelf = StructNew()>  
<cfset a = StructInsert(bookshelf, "names", titles, 1)>  
<cfset a = StructInsert(bookshelf, "values", myBook, 1)>

Here, we simply initialize bookshelf as a struct and then put two keys into it. The first key is called “names“, and we put the titles struct into this key. The second key is called “values“, and we put the myBook struct into this. We now have a struct that contains two structs, each of which contains five keys. Now, I want to sort the struct by the name of the key, so I’m going to do this:

<cfset keyList = StructKeyList(bookshelf.names)>  
<cfset keyList = ListSort(keyList, "TEXT")>

We’ve got a new function here, StructKeyList, which takes as a parameter the name of a structure. It then gets a list of all the keys and returns these in a comma delimited list. Then, we use the ListSort function to order the structure keys alphabetically. I do want to point out the method of accessing the structs contained in bookshelf, though. If you’ve worked with any C-style languages, you’ll recognize this as “dot notation”. You might even look at these structures as objects and see that you’re accessing their members. In this case, names is a member of bookshelf. Now, we’ll loop over our list and output the values in each of the two structs.

<cfloop list="#keyList#" index="key">  
 <cfoutput>  
   #bookshelf.names[key]#: #bookshelf.values[key]#<br />  
 </cfoutput>  
</cfloop>

This outputs a nice neat little listing for our book. But, we don’t often come across book stores that sell only one book. So, we’re going to need to keep track of an unlimited number of books here. In order to do this, we’ll be using our bookshelf struct, and our myBook struct; we’ll also create a new struct called myBook2 and an array called myBookArray. First, we’ll create myBook2:

<cfset myBook2 = StructNew()>  
<cfset a = StructInsert(myBook2, "title", "More ColdFusion", 1)>  
<cfset a = StructInsert(myBook2, "author", "David Medlock", 1)>  
<cfset a = StructInsert(myBook2, "description", "More ColdFusion Info", 1)>  
<cfset a = StructInsert(myBook2, "publishyear", "2005", 1)>  
<cfset a = StructInsert(myBook2, "isbn", "321654DCBA", 1)>

Now, we create a one dimensional array to store our books in.

<cfset myBookArray = ArrayNew(1)>

Next, we’ll add our books to the array:

<cfset myBookArray[1] = myBook>  
<cfset myBookArray[2] = myBook2>

Now, we have an array containing two structs, each of which contains five keys. We’ll put these books in our bookshelf, and sort them the same way we did in the last example:

<cfset a = StructInsert(bookshelf, "names", titles, 1)>  
<cfset a = StructInsert(bookshelf, "values", myBookArray, 1)>  
 
<cfset keyList = StructKeyList(bookshelf.names)>  
<cfset keyList = ListSort(keyList, "TEXT")>

Now we’ll display each of our books using two loops. First, we need to make sure both books get displayed. This means we’ll be looping over the array that we’ve stored in bookshelf.values. Then, we’ll loop over the list of keys and display the label and value for each key in the current array element:

<cfloop from="1" to="#ArrayLen(bookshelf.values)#" index="i">  
 <cfloop list="#keyList#" index="key">  
   <cfoutput>  
     #bookshelf.names[key]#: #bookshelf.values[i][key]#<br />  
   </cfoutput>  
 </cfloop>  
 <hr>  
</cfloop>

Notice that we use the dot notation to access the array length in our outer loop. Inside that loop, we loop over the list of keys in the array. The interesting part arises when we want to display the values for the title, author, and other fields. We access the bookshelf struct, then the values key of the bookshelf struct (which is an array), then the index i of the values key (which is a struct), and, finally, we get the value of key from that struct.

Are you now thoroughly confused? The first time I had to use structs at length was quite an experience. But, if you break your task down into simple steps, you’ll find that it’s actually very easy to use ColdFusion’s built-in data structures to enhance your application. Learning these elements of ColdFusion application design and programming can very quickly place you a step ahead of many other CF developers, and it can give you the tools you need to accomplish any task you’re assigned.

Built-In ColdFusion Structs

There are actually some structs that are built into ColdFusion that you probably didn’t even realize were structs. For example, did you know that the Form, URL, and CGI variable scopes are all structs? That’s right. You can easily discover what variables are in each of these structs simply by looping over the appropriate collection. (I know that all these variables appear in the ColdFusion debug information, but sometimes it’s extremely handy to know how to get this information without the aid of CF’s built-in debugging.)

Let’s look at a simple example with the CGI variables, which provide us with essential information about the environment our application runs in. Here’s the code to do this:

<cfloop collection="#CGI#" item="key">  
 <cfoutput>#key# = #CGI[key]#<br></cfoutput>  
</cfloop>

That’s it! You can run this very same code to gain information on the Form, URL, CGI, Client, Application, and Session variables. These are all structures from ColdFusion’s point of view, and therefore they are easily accessible using the above method.

That wraps up our introduction to structures in ColdFusion. As you can see, CFML provides some very powerful means of organizing, tracking, and accessing data. You may find that you don’t use these methods very often, but they can often be your saving grace when you’re in a pinch for a solution to a difficult problem. I’ve found structs and arrays to be very helpful when you have complex data that needs to be displayed in unconventional ways. Store these little tidbits of information somewhere in the back of your brain, bookmark this article in your “ColdFusion Resources” folder (I know you have one), and you’ve got one more tool in your development arsenal.

Frequently Asked Questions (FAQs) about ColdFusion II Structs

What is the difference between arrays and structs in ColdFusion?

In ColdFusion, both arrays and structs are used to store data, but they do so in different ways. An array is an ordered list of elements, each identified by a numerical index. On the other hand, a struct (short for structure) is an unordered collection of data where each element is identified by a unique key. This key can be a string or a number. The main advantage of using structs is that you can access its elements directly using their keys, without needing to know their position in the collection.

How do I create a struct in ColdFusion?

Creating a struct in ColdFusion is straightforward. You can use the StructNew() function to create an empty struct, and then add elements to it using the dot notation or the bracket notation. Here’s an example:

<cfset myStruct = StructNew()>
<cfset myStruct.name = "John">
<cfset myStruct["age"] = 30>

In this example, we’ve created a struct named myStruct and added two elements to it: name and age.

Can I nest structs within structs in ColdFusion?

Yes, you can nest structs within structs in ColdFusion. This is useful when you want to group related data together. For example, you can create a struct to represent a person, and within that struct, you can create another struct to represent the person’s address. Here’s an example:

<cfset person = StructNew()>
<cfset person.name = "John">
<cfset person.address = StructNew()>
<cfset person.address.street = "123 Main St">
<cfset person.address.city = "Anytown">

In this example, the person struct contains a nested address struct.

How do I access elements in a struct?

You can access elements in a struct using the dot notation or the bracket notation. The dot notation is more common and easier to read, but the bracket notation allows you to use keys that are not valid CFML identifiers or keys that are stored in variables. Here’s an example:

<cfset myStruct = StructNew()>
<cfset myStruct.name = "John">
<cfoutput>#myStruct.name#</cfoutput> <!-- Outputs: John -->
<cfoutput>#myStruct["name"]#</cfoutput> <!-- Outputs: John -->

In this example, we’re accessing the name element in the myStruct struct.

Can I modify elements in a struct?

Yes, you can modify elements in a struct by simply assigning a new value to them. If the key doesn’t exist, it will be created. Here’s an example:

<cfset myStruct = StructNew()>
<cfset myStruct.name = "John">
<cfset myStruct.name = "Jane"> <!-- Modifies the name element -->
<cfoutput>#myStruct.name#</cfoutput> <!-- Outputs: Jane -->

In this example, we’re modifying the name element in the myStruct struct.

How do I delete elements from a struct?

You can delete elements from a struct using the StructDelete() function. This function takes two arguments: the struct and the key of the element to delete. Here’s an example:

<cfset myStruct = StructNew()>
<cfset myStruct.name = "John">
<cfset StructDelete(myStruct, "name")> <!-- Deletes the name element -->
<cfoutput>#myStruct.name#</cfoutput> <!-- Outputs: [empty string] -->

In this example, we’re deleting the name element from the myStruct struct.

How do I check if a struct contains a specific key?

You can check if a struct contains a specific key using the StructKeyExists() function. This function takes two arguments: the struct and the key to check. It returns true if the key exists in the struct, and false otherwise. Here’s an example:

<cfset myStruct = StructNew()>
<cfset myStruct.name = "John">
<cfoutput>#StructKeyExists(myStruct, "name")#</cfoutput> <!-- Outputs: YES -->
<cfoutput>#StructKeyExists(myStruct, "age")#</cfoutput> <!-- Outputs: NO -->

In this example, we’re checking if the name and age keys exist in the myStruct struct.

How do I get all the keys in a struct?

You can get all the keys in a struct using the StructKeyArray() function. This function takes one argument: the struct. It returns an array containing all the keys in the struct. Here’s an example:

<cfset myStruct = StructNew()>
<cfset myStruct.name = "John">
<cfset myStruct.age = 30>
<cfset keys = StructKeyArray(myStruct)>
<cfoutput>#ArrayToList(keys)#</cfoutput> <!-- Outputs: name,age -->

In this example, we’re getting all the keys in the myStruct struct.

How do I iterate over a struct?

You can iterate over a struct using the cfloop tag with the collection attribute set to the struct and the item attribute set to a variable that will hold the current key. Inside the loop, you can access the current value using the struct and the current key. Here’s an example:

<cfset myStruct = StructNew()>
<cfset myStruct.name = "John">
<cfset myStruct.age = 30>
<cfloop collection="#myStruct#" item="key">
<cfoutput>#key#: #myStruct[key]#</cfoutput> <!-- Outputs: name: John, age: 30 -->
</cfloop>

In this example, we’re iterating over the myStruct struct.

How do I copy a struct?

You can copy a struct using the Duplicate() function. This function takes one argument: the struct to copy. It returns a new struct that is a deep copy of the original struct. Here’s an example:

<cfset myStruct = StructNew()>
<cfset myStruct.name = "John">
<cfset myStruct.age = 30>
<cfset copy = Duplicate(myStruct)>
<cfoutput>#copy.name#</cfoutput> <!-- Outputs: John -->
<cfoutput>#copy.age#</cfoutput> <!-- Outputs: 30 -->

In this example, we’re copying the myStruct struct.