Literal arrays and structures

I admit it. When I was a young programmer Arrays confused the heck out of me so I coded around them whenever possible. As the decades passed, I finally came to understand and even like using them...with one caveat. Creating an array is a pain. It took more code to create them than seemed necessary. Notice I used past tense there? Adobe has made it much easier to create arrays AND structures. Why is this cool? Because it cuts down on the coding needed by a ton, and is more in-sync with a lot of other languages out there! Check this out....


Arrays the Old Way

This is the old way to create a one-dimensional array. Yes, there are two and three-dimensional arrays but I am not going to get into that here.


<cfset oldArray = arrayNew(1)>
<cfset oldArray[1] = "William Shatner">
<cfset oldArray[2] = "Patrick Stewart">
<cfset oldArray[3] = "Avery Brooks">
<cfset oldArray[4] = "Kate Mulgrew">
<cfset oldArray[5] = "Scott Bakula">

This code creates an array object that looks like so when dumped to the screen:



<cfdump var="#oldArray#">


array
1 William Shatner
2 Patrick Stewart
3 Avery Brooks
4 Kate Mulgrew
5 Scott Bakula

Using array syntax we can output specific values like so:



<cfoutput>#oldArray[3]#</cfoutput>


Which will display the value....

Avery Brooks


Now, this isn't new or anything. If you have ever created an array in ColdFusion then this should look at least a little familiar. A one-dimensional array like this is pretty easy to understand and access in CFML, but once you get into multi-dimensional arrays or arrays of arrays it gets much harder and more annoying to use. Not to mention the code itself is verbose.

Flash forward to 2014 when Adobe released ColdFusion 11. They included a way to create arrays in what is called literal syntax. At first it may seem confusing but once you do it a few times it becomes clear that this is a much easier way to create arrays.


<cfset trekTech = ["Phasers","Photon Torpedoes","Tricorder","Transporter","Holodeck"]>

<cfdump var="#trekTech#">

<cfoutput>#trekTech[3]#</cfoutput>


You can see the above code, the first line creates an array with the same number of values that it took six lines to create using the old method. Output and dumping the object are, of course, done the same way.

You can see the basic syntax of creating an array this way is arrayName = ["value 1","value 2","value 3"], which is almost the same as JavaScript and even C++.

array
1 Phasers
2 Photon Torpedoes
3 Tricorder
4 Transporter
5 Holodeck

Tricorder


Arrays of Arrays....or an Array containing other arrays

Now, suppose we have more data and want to assign it to an array containing arrays. Why would we do this? Eh, we probably wouldn't unless we are trying to use external data or organize a mess into something useful. See here where I create a series of arrays about a certain TV show for each of the five series.


<cfset theShow = [["Star Trek","1966-1969","Captain Kirk","LtCdr Spock"],["Star Trek the Next Generation","1987-1994","Captain Picard","Cdr Riker"],["Star Trek Deep Space Nine","1993-1999","Captain Sisko","Maj Kira"],["Star Trek Voyager","1995-2001","Captain Janeway","Cdr Chakotay"],["Star Trek Enterprise","2001-2005","Captain Archer","Sub-Cdr T'Pol"]]>

<cfdump var="#theShow#">

<cfoutput>#theShow[1][1]# #theShow[1][2]# #theShow[1][3]#</cfoutput>


array
1
array
1 Star Trek
2 1966-1969
3 Captain Kirk
4 LtCdr Spock
2
array
1 Star Trek the Next Generation
2 1987-1994
3 Captain Picard
4 Cdr Riker
3
array
1 Star Trek Deep Space Nine
2 1993-1999
3 Captain Sisko
4 Maj Kira
4
array
1 Star Trek Voyager
2 1995-2001
3 Captain Janeway
4 Cdr Chakotay
5
array
1 Star Trek Enterprise
2 2001-2005
3 Captain Archer
4 Sub-Cdr T'Pol

Star Trek 1966-1969 Captain Kirk

As you can see it's basically arrayName = [["array 1 item 1","array 1 item 2"],["array 2 item 1","array 2 item 2"]]. For some reason it is much easier to keep track of than tons of lines of CFSET tags.


Structures the same way - almost

Creating structures in ColdFusion has always been a bit easier than arrays. Remember that structures are assigned to a key. Let's say you create a structure named USER and you want to assign a person's username to it. You would set USERS.USERNAME equal to the username, etc. Overall structures are way easier because we use similar syntax when scoping variables all day long. So USERS.USERNAME is the same basic syntax as URL.PAGENUMBER or CGI.REMOTE_HOST. Make sense?

When should you use an array v.s. a structure? Think of arrays as a spreadsheet of data stored in memory. Think of structures as one row of that spreadsheet stored in memory. Structures are almost like storing a form submission in memory and using the data whenever you like.

In the past, I have used arrays to organize messy data into a clean structure. I recall having several comma separated lists of courses for half a dozen instructors - so I created an array that contained the instructors and then an array inside each that contained their courses.

Structures come in handy when you need to retain some info for a user. Say you have a form and the user fills it in and then submits - but they screwed something up and have to go back. Normally they would need to fill the form in again. Instead you create a structure in the session scope with blank values, then fill in the form with those values by default. When the user submits their data you update the structure in the session with their info and then when you send them back the form is pre-populated with their previous info.

Of course, ask me if you need the code for either of these.

Okay, back to the topic at hand. Creating a structure the old way was pretty easy - yet still pretty hefty in lines of code.


<cfset oldStruct = structNew()>
<cfset oldStruct.captain = "Picard">
<cfset oldStruct.firstOfficer = "Riker">
<cfset oldStruct.engineer = "La Forge">
<cfset oldStruct.medical = "Crusher">
<cfset oldStruct.operations = "Data">

<cfdump var="#oldStruct#">



On Star Trek the Next Generation the Captain is <cfoutput>#oldStruct.captain#</cfoutput>.




struct
CAPTAIN Picard
ENGINEER La Forge
FIRSTOFFICER Riker
MEDICAL Crusher
OPERATIONS Data

On Star Trek the Next Generation the Captain is Picard.


Again, fairly verbose. Creating structures with literal syntax is almost the same as creating an array.


<cfset theShips = [tos:"NCC-1701",tng:"NCC-1701-D",ds9:"NX-74205",voy:"NCC-74656",ent:"NX-01"]>

<cfdump var="#theShips#">

<cfoutput>#theShips.voy#</cfoutput>


struct (ordered)
TOS NCC-1701
TNG NCC-1701-D
DS9 NX-74205
VOY NCC-74656
ENT NX-01

NCC-74656


Structure of Structures...or a structure containing other structures.

You can create a structure that contains structures in CF. Heck, you can create structures that contain arrays, arrays that contain structures, arrays that contain arrays that contain structures...and on and on. My tip is to keep it simple. Simple is always the best solution.

Here's how we would create a structure that contains structures.


<cfset trekchars = [
	TOS : [Captain : "Kirk", FirstOfficer : "Spock", Helm : "Sulu", Security : "Checkov", Medical : "McCoy", Engineer : "Scotty", Ops : "Uhuru"],
	TNG : [Captain : "Picard", FirstOfficer : "Riker", Helm : "Varies", Security : "Worf", Medical : "Crusher", Engineer : "La Forge", Ops : "Data"],
	DS9 : [Captain : "Sisko", FirstOfficer : "Kira", Science : "Dax", Security : "Odo", Medical : "Bashir", Engineer : "O'Brein", Bar : "Quark"],
	VOY : [Captain : "Janeway", FirstOfficer : "Chakotay", Helm : "Paris", Security : "Tuvok", Medical : "Doctor", Engineer : "Torres", Ops : "Kim"],
	ENT : [Captain : "Archer", FirstOfficer : "T'Pol", Helm : "Mayweather", Security : "Reed", Medical : "Phlox", Engineer : "Tucker", Ops : "Sato"]
	]>

<cfdump var="#trekchars#">

"Time's a wasting!", said <cfoutput>#trekchars.ENT.Captain#</cfoutput>


struct (ordered)
TOS
struct (ordered)
CAPTAIN Kirk
FIRSTOFFICER Spock
HELM Sulu
SECURITY Checkov
MEDICAL McCoy
ENGINEER Scotty
OPS Uhuru
TNG
struct (ordered)
CAPTAIN Picard
FIRSTOFFICER Riker
HELM Varies
SECURITY Worf
MEDICAL Crusher
ENGINEER La Forge
OPS Data
DS9
struct (ordered)
CAPTAIN Sisko
FIRSTOFFICER Kira
SCIENCE Dax
SECURITY Odo
MEDICAL Bashir
ENGINEER O'Brein
BAR Quark
VOY
struct (ordered)
CAPTAIN Janeway
FIRSTOFFICER Chakotay
HELM Paris
SECURITY Tuvok
MEDICAL Doctor
ENGINEER Torres
OPS Kim
ENT
struct (ordered)
CAPTAIN Archer
FIRSTOFFICER T'Pol
HELM Mayweather
SECURITY Reed
MEDICAL Phlox
ENGINEER Tucker
OPS Sato

"Time's a wasting.", said Archer


A Structure containing an array

Okay let's see if we can blow your minds by combining arrays and structures. Let's say we wanted to store a ton of info in a relatively structured collection. I am going to use a structure for that since they have unique keys. This will allow me to use the key name instead of array element positions like [1][2][3], etc. Because we need to collect the data that will be populating the main structure we need to create this in reverse order.

First I need to create an array of cast members for each show. Without this we can't populate the seriesData structure.


<cfset seriesCast = [["William Shatner","Captain James T. Kirk"],["Leonard Nimoy","Lt.Cdr Spock"],["James Doohan","Lt.Cdr Montomery Scott"]]>

Next we will create the seriesData structure that, you will notice, contains the array of cast members we just created.


<cfset seriesData = [airdate : "1966-1969", title : "Star Trek the Original Series", cast : seriesCast]>

Now for good measure we will add another series to the data so we can see how more than one collection of data will appear.


<cfset seriesCast2 = [["Patrick Stewart","Captain Jean Luc Picard"],["Jonathan Frakes","Cmdr William T. Riker"],["Brent Spiner","Lt.Cdr Data"],["Dr. Beverly Crusher","Gates McFadden"]]>
<cfset seriesData2 = [airdate : "1987-1993", title : "Star Trek the Next Generation", cast : seriesCast2]>

Now we populate a structure with the created elements.


<cfset seriesInfo = [TOS : [seriesData],TNG : [seriesData2]]>

Let's see how disgusting this thing looks and how we can extract data from it.

struct (ordered)
TOS
array
1
struct (ordered)
AIRDATE 1966-1969
TITLE Star Trek the Original Series
CAST
array
1
array
1 William Shatner
2 Captain James T. Kirk
2
array
1 Leonard Nimoy
2 Lt.Cdr Spock
3
array
1 James Doohan
2 Lt.Cdr Montomery Scott
TNG
array
1
struct (ordered)
AIRDATE 1987-1993
TITLE Star Trek the Next Generation
CAST
array
1
array
1 Patrick Stewart
2 Captain Jean Luc Picard
2
array
1 Jonathan Frakes
2 Cmdr William T. Riker
3
array
1 Brent Spiner
2 Lt.Cdr Data
4
array
1 Dr. Beverly Crusher
2 Gates McFadden

Yeah, that will hurt your brain. I know, I said keep it simple, but understanding the complex way helps the simple stuff seem easier! Digging into this data, if we want to access the title from the first structure key we would use #seriesInfo.TOS[1].title#. To access the array of cast members we need to dig deeper with #seriesInfo.TOS[1].cast#, or to get the first actor and character we would use #seriesInfo.TOS[1].cast[1][1]# #seriesInfo.TOS[1].cast[1][2]#

Star Trek the Original Series William Shatner

Since we know where that array of cast members is we can actually loop over it. Check this out.


<cfoutput>#seriesInfo.TOS[1].title# aired from #seriesInfo.TOS[1].airdate#</cfoutput>

<cfoutput>
<ul>
<cfloop array="#seriesInfo.TOS[1].cast#" item="person">
	<li>#person[2]# played by #person[1]#</li>
</cfloop>
</ul>
</cfoutput>

Star Trek the Original Series aired from 1966-1969

Again, I don't condone using this super complicated method unless you have no alternative. More than likely you would have to use this when given data from another source.


I am sure this is probably clear as mud...but using the basic syntax below should allow you to create snippets so you can juist squirt the code into your apps and make it work for you.

Simple Structure

<cfset STRUCTNAME = [FIRSTKEY:"VALUE HERE",NEXTKEY:"NEXT VALUE HERE"]>
<cfoutput>#STRUCTNAME.FIRSTKEY#</cfoutput>

Complex Structure

<cfset COMPLEXSTRUCT = [
	KEY1 : [SUBSTRUCT_KEY1 : "VALUE 1", SUBSTRUCT_KEY2 : "VALUE 2"],
	KEY2 : [SUBSTRUCT_KEY1 : "VALUE A", SUBSTRUCT_KEY2 : "VALUE B"],
	]>

<cfoutput>#COMPLEXSTRUCT.KEY1.SUBSTRUCT_KEY1#</cfoutput>


Simple Array

<cfset ARRAYNAME = ["VALUE ONE","VALUE TWO"]>

<cfoutput>#ARRAYNAME[2]#</cfoutput>


Complex Array

<cfset COMPLEXARRAYNAME = [["VALUE 1.1","VALUE 1.2"],["VALUE 2.1","VALUE 2.2"],["VALUE 3.1","VALUE 3.2"]]>

<cfoutput>#COMPLEXARRAYNAME[1][1]# #COMPLEXARRAYNAME[2][2]# #COMPLEXARRAYNAME[3][1]#</cfoutput>