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 BoxLang 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.
📋 Table of Contents
🔑 Key-Value Pairs
A structure is an unordered collection where the data gets organized as a key and value pair. BoxLang syntax for structures follows the following syntax:
Tip Please note that = sign and : are interchangeable in BoxLang. So you can use any to define your structures.
Since BoxLang 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 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
Retrieving values from structures can be done via dot or array notation or the structFind() function. Let's explore these approaches:
🔢 Array Notation
⚫ Dot Notation
structFind( structure, key, [defaultValue ] )
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/struct/structfind
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.
BoxLang 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://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/struct/structget
🛡️ Safe Navigation
BoxLang 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.
📤 Setting Values
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://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/struct/structinsert, https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/struct/structupdate)
Tip You can use the toString() call on any structure to get a string representation of its keys+values: produce.toString()
📚 Struct Built-In Functions (BIFs)
BoxLang provides a comprehensive set of struct BIFs organized by functionality. All struct BIFs can be called as member methods on Struct objects.
🔨 Creation & Configuration Functions
structNew()
Create new struct
structNew("ordered") → [:]
structCopy()
Create shallow copy
structCopy(myStruct)
structToSorted()
Create sorted copy
structToSorted(myStruct, "text")
➕ Modification Functions
structInsert()
Insert key-value pair
structInsert(struct, "key", "value")
structUpdate()
Update existing key
structUpdate(struct, "key", "newValue")
structAppend()
Merge structs
structAppend(struct1, struct2)
structDelete()
Remove key
structDelete(struct, "key")
structClear()
Remove all keys
structClear(struct)
🔍 Search & Filter Functions
structFind()
Find value by key
structFind(struct, "key") → value
structGet()
Get with dot notation
structGet("struct.nested.key")
structFindKey()
Find keys matching value
structFindKey(struct, "searchValue")
structFindValue()
Find values matching criteria
structFindValue(struct, "pattern")
structKeyExists()
Check if key exists
structKeyExists(struct, "key") → true
structFilter()
Filter by condition
structFilter(struct, (k,v) -> v > 5)
structEvery()
Test all entries
structEvery(struct, (k,v) -> v > 0) → true
structSome()
Test any entry
structSome(struct, (k,v) -> v > 10) → true
structNone()
Test no entries match
structNone(struct, (k,v) -> v < 0) → true
🔄 Transformation Functions
structMap()
Transform values
structMap(struct, (k,v) -> v * 2)
structReduce()
Reduce to single value
structReduce(struct, (acc,k,v) -> acc + v, 0)
structKeyTranslate()
Translate keys
structKeyTranslate(struct, mapping)
structToQueryString()
Convert to query string
structToQueryString(struct) → "key1=val1&key2=val2"
📊 Sorting Functions
structSort()
Sort keys to array
structSort(struct, "text") → ["key1", "key2"]
📏 Information Functions
structCount()
Get key count
structCount(struct) → 5
structIsEmpty()
Check if empty
structIsEmpty(struct) → false
structKeyArray()
Get keys as array
structKeyArray(struct) → ["key1", "key2"]
structKeyList()
Get keys as list
structKeyList(struct) → "key1,key2"
structValueArray()
Get values as array
structValueArray(struct) → [val1, val2]
structEquals()
Compare structs
structEquals(struct1, struct2) → true
structIsCaseSensitive()
Check case sensitivity
structIsCaseSensitive(struct) → false
structIsOrdered()
Check if ordered
structIsOrdered(struct) → true
structGetMetadata()
Get struct metadata
structGetMetadata(struct)
🔗 Iteration Functions
structEach()
Iterate each entry
structEach(struct, (k,v) => println(k & ":" & v))
Complete Reference: For detailed documentation of each function, visit the Struct BIF Reference.
⚙️ Member Functions
All struct BIFs can be called as member functions on Struct objects for cleaner, more fluent code:
🔧 Java Map Methods
Since BoxLang structs implement the java.util.Map interface, you have access to all Java Map methods:
🎯 BoxLang Struct Native Methods
The BoxLang Struct type provides additional methods beyond standard Java Map operations:
🔑 Key Differences: String Keys vs Key Objects
Get value
struct.get("name")
struct.get(Key.of("name"))
Check existence
struct.containsKey("name")
struct.containsKey(Key.of("name"))
Set value
struct.put("name", val)
struct.put(Key.of("name"), val)
Remove
struct.remove("name")
struct.remove(Key.of("name"))
Case handling
Always case-insensitive*
Respects struct type
*Unless the struct is case-sensitive type
🗂️ Structure Types
In BoxLang, not only can you create case-insensitive unordered structures but also the following types using the structNew() function (https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/struct/structnew)
case-sensitive
normal
ordered or linked
ordered-case-sensitive
soft
synchronized
weak
Here is the signature for the structnew() function:
Now let's create some different types of structures
💻 Literal Syntax
You can also use literal syntax for some of these types:
Tip: Check out all the structure BIF reference and member functions for more capabilities.
🔁 Looping Over Structures
You can use different constructs for looping over structures:
forloopsloopconstructseach()closures
⚡ Multi-Threaded Looping
BoxLang 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.
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.
Even though this approach to multi-threaded looping is easy, it is not performant and/or flexible. Under the hood, the BoxLang uses 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.
🚀 ColdBox Futures Parallel Programming
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 BoxLang application and leveraging our async programming constructs, which behind the scenes, leverage the entire Java Concurrency and Completable Futures frameworks.
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. Thefnargument 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. ThetimeUnitcan be days, hours, microseconds, milliseconds, minutes, nanoseconds, and seconds. The default is milliseconds.
✨ Trailing Commas
BoxLang supports trailing commas when defining array and struct literals. Just in case you miss a dangling comma, we won't shout at you!
👂 Change Listeners
All arrays and structures offer the ability to listen to changes to themselves. This is all done via our $bx metadata object available on all arrays/structures. You will call the registerChangeListener() function to register a closure/lambda that will listen to changes on the struct. You can listen:
To all changes in the structure
To a specific key in the structure
However, you must EXPLICITLY return the value that will be stored in the change.
Please note that this change listener will fire on every struct access, so ensure it's performant.
The signature of the closure/lambda is the following
Please note that the Key is a BoxLang Key object, which simulates a case-insensitive string. You can use methods on it like:\
getName()getNameNoCase()toString()
Here are a few simple examples:
Here is another example when listening to a specific index:
Last updated
Was this helpful?

