Arrays
An array is a data structure consisting of a collection of elements.
Almost every programming language allows you to represent different types of collections. In BoxLang, we have three types of collections: arrays, structures, and queries.
An array is a number-indexed list. Imagine you had a blank piece of paper and drew a set of three small boxes in a line:
You could number each one by its position from left to right:
Then put strings in each box:
We have a three-element Array. BoxLang arrays can grow and shrink dynamically at runtime, just like Array Lists or Vectors in Java, so if we added an element, it’d usually go on the end or be appended at the end.
If you asked the array for the element in position two, you’d get back Lunch
. Ask for the last element, and you’ll get back: Dessert
.
The Story of One
Now, have you detected something funny with the ordering of the elements? Come on, look closer....... They start with 1
and not 0
, now isn't that funny. BoxLang is one of the few languages where array indexes start at 1
and not 0
. So if you have a PHP, Ruby, or Java background, remember that 1
is where you start. Is this good or bad? Well, we will refrain from pointing fingers for now.
All arrays in BoxLang are passed by passed by reference. Please remember this when working with arrays and passing them to functions. There is also the passby=reference|value
attribute to function arguments where you can decide whether to pass by reference or value.
Arrays in Code
Let's go ahead and model some code in BoxLang using our fancy REPL tool CommandBox:
Check it out:
The array was created by putting pieces of data between square brackets (
[]
) and separated by commasWe added an element to the array using the member function
append()
We fetched the element at a specific position by using square brackets (
[ x ]
) and replacedx
with the index, we wantedWe retrieved the size of the array by using the member function
len()
We searched the contents of the array using the member function
findNoCase()
, and it gave us the index position of the element in the array.
Please note that all member functions can also be used as traditional array functions. However, member functions look much better for readability.
Tip: You can use the toString()
call on any array to get a string representation of its values: grid.toString()
Multi-Dimensional Arrays
To create grids or matrix constructs, you must create two-dimensional arrays. This gives you an x and y axis of data. You will do so using the arrayNew( dimensions = max 3 )
method:
Tip: BoxLang only supports two and three-dimensional arrays, so you can easily represent x, y, and z axis.
Common Methods
The best way to learn about using arrays is to check out the available member functions and array functions.
Typed Arrays
BoxLang also allow you to create strongly typed arrays. This is useful if you want to determine the array's contents specifically. Similar to generics in Java.
This syntax will allow you to define an array as being of a certain type and how many dimensions it has.
If you want to use typed arrays, then you will have to declare it via the ArrayNew()
method via the second argument:
Please note that BoxLang will try to cast values automatically into the type defined by the array container.
Tip: By default, all BoxLang arrays are Unsynchronized. That means they are not thread-safe when accessing the data from multiple threads or shared scopes.
Unsynchronized arrays are about 93% faster due to lock avoidance. So with much power comes much responsibility.
Array Types
The allowed types are:
Array
Binary
Boolean
Component
Class by Name / SubType
Date / Datetime
Function
Numeric
Query
String
Struct
Literal Syntax
BoxLang also supports a way to make a declaration of a typed array using a literal syntax:
This is a nice way to declare them literally:
Negative Indices
BoxLang also supports the concept of negative indices. This allows you to retrieve the elements from the end of the array backward. So you can easily count back instead of counting forwards:
Array Slices
BoxLang supports the slicing of an array via the arraySlice()
method or the slice()
member function, respectively.
Looping Over Arrays
You can use different constructs for looping over arrays:
for
loopsloop
constructseach()
closures
Multi-Threaded Looping
BoxLang allows you to leverage the each()
operations in a multi-threaded fashion. The arrayEach()
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 your callback will now 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 when accessing shared scopes and/or resources.
Even though this approach to multi-threaded looping is easy, it is not performant and/or flexible. Under the hood, the engine uses a single thread executor for each execution. They 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 item 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.
Spread Operator
Arrays also allow the usage of the spread operator syntax to quickly copy all or part of an existing array or object into another array or object. This operator is used by leveraging three dots ...
in specific expressions.
The Spread syntax allows an iterable such as an array expression or string, to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected. Here are some examples to help you understand this operator:
Function Calls
Array Definitions
Rest Operator
The rest operator is similar to the spread operator but behaves oppositely. Instead of expanding the literals, it contracts them into an array you designate via the ...{name}
syntax. You can use this to define endless arguments for a function, for example. In this case, I can create a dynamic findBy
function that takes in multiple criteria name-value pairs.
Last updated