Structures
Collection of key-value pairs; a data dictionary
A structure is a collection of data where each element of data is addressed by a name or key and it can hold a value of any type. Like a dictionary but on steroids:
// Create a struct via function
myStruct = structnew()
// Create a struct via literal syntax
myStruct = {}
Tip Underneath the hood, all CFML structures are based on the
java.util.Map
interface. So if you come from a Java background, structures are untyped HashMaps
.As an analogy, think about a refrigerator. If we’re keeping track of the produce inside the fridge, we don’t really care about where the produce is in, or basically: order doesn’t matter. Instead, we organize things by name, which are unique, and each name can have any value. The name grapes might have the value 2, then the name lemons might have the value 1, and eggplants the value 6.
All CFML structures are passed to functions as memory references, not values. Keep that in mind when working with structures. There is also the
passby=reference|value
attribute to function arguments where you can decide whether to pass by reference or value.A structure is an unordered collection where the data gets organized as a key and value pair. CFML syntax for structures follows the following syntax:
produce = {
grapes = 2,
lemons = 1,
eggplants = 6
};
Tip Please note that
=
sign and :
are interchangeable in CFML. So you can use any to define your structures.Since CFML is a case-insensitive language, the above structure will store all keys in uppercase. If you want the exact casing to be preserved in the structure, then surround the keys with quotes (
"
).The exact casing is extremely important if you will be converting these structures into JSON in the future.
produce = {
"grapes" = 2,
"lemons" = 1,
"eggplants" = 6
};

The key is the address, and the value is the data at that address. Please note that the value can be ANYTHING. It can be an array, an object, a simple value, or even an embedded structure. It doesn't matter.
Retrieving values from structures can be done via dot or array notation or the
structFind()
function. Let's explore these approaches:writeOutput( "I have #produce[ "grapes" ]# grapes in my fridge!" );
writeOutput( "I have #produce[ "eggplants" ]# eggplants in my fridge!" );
writeOutput( "I have #produce.grapes# grapes in my fridge!" );
writeOutput( "I have #produce.eggplants# eggplants in my fridge!" );
// Member Function
writeOutput( "I have #produce.find( "grapes" )# grapes in my fridge!" );
writeOutput( "I have #produce.find( "eggplants" )# eggplants in my fridge!" );
// Global Function
writeOutput( "I have #structFind( produce, "grapes" )# grapes in my fridge!" );
writeOutput( "I have #structFind( produce, "eggplants" ) eggplants in my fridge!" );
However, please be aware that when dealing with native Java hashmaps, we recommend using array notation as the case is preserved in array notation, while in dot notation, it does not.
CFML offers also the
structGet()
function which will search for a key or a key path. If there is no structure or array present in the path, this function creates structures or arrays to make it a valid variable path. https://cfdocs.org/structgetCFML also supports the concept of safe navigation when dealing with structures. Sometimes it can be problematic when using dot notation on nested structures since some keys might not exist or be
null
. You can avoid this pain by using the safe navigation operator ?.
instead of the traditional .
, and combine it with the elvis operator ?:
so if null, then returning a value.user = { age : 40 }
echo( user.age ) // 40
echo( user.salary ) // throws exception
echo( user?.salary ) // nothing, no exception
echo( user?.salary ?: 0 ) // 0
I can also set new or override structure values a la carte. You can do so via array/dot notation or via the
structInsert(), structUpdate()
functions (https://cfdocs.org/structinsert, https://cfdocs.org/structupdate)// new key using default uppercase notation
produce.apples = 3;
// new key using case sensitive key
produce[ "apples" ] = 3;
// I just ate one grape, let's reduce it
produce[ "grapes" ] = 1; // or
produce.grapes--;
produce.insert( "newVeggie", 2 )
produce.update( "newVeggie", 1 )
structInsert( produce, "carrots", 5 )
structUpdate( produce, "carrots", 2 )
Tip You can use the
toString()
call on any structure to get a string representation of its keys+values: produce.toString()
CFML also offers some useful methods when dealing with structures:
Function | Member Function |
---|---|
structIsEmpty() | isEmpty() |
structCount() | count() |
Here are some great functions that deal with getting all key names, and key values or checking for existence:
Function | Member Function |
---|---|
structKeyArray() | keyArray() |
structKeyList() | keyList() |
structKeyExists() | keyExists() |
produce.keyArray()
.each( (item) => echo( item ) )
writeOutput( "My shopping bag has: #produce.keyList()# " )
writeOutput( "Do you have carrots? #produce.keyExists( 'carrots' )#" )
In CFML, not only can you create case-insensitive unordered structures but also the following types using the
structNew()
function (https://cfdocs.org/structnew)Type | Adobe 2018 | Adobe 2021 | Lucee |
---|---|---|---|
casesensitive | |||
normal | |||
ordered or linked | |||
ordered-casesensitive | |||
soft | |||
synchronized | |||
weak |
Here is the signature for the
structnew()
function on Adobe engines:structNew( [type[[,sortType][,sortOrder][,localeSensitive]|[,callback]]] )
structNew( [type, [onMissingKey] ] )
Now let's create some different types of structures
produce = structNew();
pickyProduce = structNew( "casesensitive" )
queue = structNew( "ordered" )
pickyQueue = structNew( "ordered-casesensitive" )
linkedList = structNew( 'ordered' );
cache = structnew( 'soft' );
You can also use literal syntax for some of these types:
// ordered struct
myStruct = [:] or [=]
// Case sensitive struct ACF Only
myStruct = ${}
// Case sensitive ordered struct ACF Only
myStruct = $[=]
Once you create structures, you can use them in many funky ways. Please check out all the structure functions and all the structure modern member functions that are available to you.

As you can see, there are many cool methods for detecting keys, values, lengths, counts, etc. A very cool method is
keyArray()
which gives you the listing of keys as an array:
You can use different constructs for looping over structures:
for
loopsloop
constructseach()
closures
for( var key in produce ){
systemOutput( "I just had #produce[ key ]# #key#" );
}
produce.each( function( key, value ){
systemOutput( "I just had #value# #key#" );
} );
As of now, only Lucee and Adobe 2021 allows you to leverage the
each()
operations in a multi-threaded fashion. The structEach()
or each()
functions allow for a parallel
and maxThreads
arguments so the iteration can happen concurrently on as many maxThreads
as supported by your JVM.structEach( struct, callback, parallel:boolean, maxThreads:numeric );
each( collection, callback, parallel:boolean, maxThreads:numeric );
This is incredibly awesome as now your callback will be called concurrently! However, please note that once you enter concurrency land, you should shiver and tremble. Thread concurrency will be of the utmost importance, and you must ensure that var scoping is done correctly and that appropriate locking strategies are in place.
myStruct.each( function( key, value ){
myservice.process( value );
}, true, 20 );
Even though this approach to multi-threaded looping is easy, it is not performant and/or flexible. Under the hood, the engines use a single thread executor for each execution, do not allow you to deal with exceptions, and if an exception occurs in an element processor, good luck; you will never know about it. This approach can be verbose and error-prone, but it's easy. You also don't control where the processing thread runs and are at the mercy of the engine.
If you would like a functional and much more flexible approach to multi-threaded or parallel programming, consider using the ColdBox Futures approach (usable in ANY framework or non-framework code). You can use it by installing ColdBox or WireBox into any CFML application and leveraging our
async
programming constructs, which behind the scenes, leverage the entire Java Concurrency and Completable Futures frameworks.
Parallel Computations
ColdBox HMVC Documentation
ColdBox Futures and Async Programming
Here are some methods that will allow you to do parallel computations:
all( a1, a2, ... ):Future
: This method accepts an infinite amount of future objects, closures, or an array of closures/futures to execute them in parallel. When you call on it, it will return a future that will retrieve an array of the results of all the operations.allApply( items, fn, executor ):array
: This function can accept an array of items or a struct of items of any type and apply a function to each of the items in parallel. Thefn
argument receives the appropriate item and must return a result. Consider this a parallelmap()
operation.anyOf( a1, a2, ... ):Future
: This method accepts an infinite amount of future objects, closures, or an array of closures/futures and will execute them in parallel. However, instead of returning all of the results in an array likeall()
, this method will return the future that executes the fastest! Race Baby!withTimeout( timeout, timeUnit )
: Apply a timeout toall()
orallApply()
operations. ThetimeUnit
can be days, hours, microseconds, milliseconds, minutes, nanoseconds, and seconds. The default is milliseconds.
Last modified 6mo ago