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:
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.
All BoxLang 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.
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 exact casing is extremely important if you will be converting these structures into JSON in the future.
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()
Checking Contents & Size
BoxLang also offers some useful methods when dealing with structures:
Function | Member Function |
---|---|
|
|
|
|
Key Values & Existence
Here are some great functions that deal with getting all key names, and key values or checking for existence:
Function | Member Function |
---|---|
|
|
|
|
|
|
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:
Common Methods
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:
Looping Over Structures
You can use different constructs for looping over structures:
for
loopsloop
constructseach()
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. 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 updated