# Code Kata: Recursively Flattening A Deep Array In Lucee CFML

By on
Tags:

Yesterday, I looked at flattening an array in ColdFusion. That post was more a look at the available syntax options with a variadic function and less a look at the actual Array flattening algorithm. And, it only flattened to a single depth. As a fast-follow, I wanted to look at what it would take to recursively flatten a deep array, with nested array elements, in Lucee CFML.

The Function signature on this recursive `flatten()` method is based on the `Array.prototype.flat()` method in JavaScript. It takes the Array to flatten and an optional depth that defaults to `1`:

`flatten( values [, depth = 1 ] ) : Array`

When this algorithm encounters a nested Array in the `values` argument, it will look to see if it has reached its maximum depth. If not, it will recursively flatten the nested Array and then merge it into current results. If the algorithm has reached its maximum depth (the "base case"), the nested Array will simply be appended to the results, no further flattening performed.

As a convenience, I'm going to add some metadata to the Function - `MAX_DEPTH` - which is just a large integer that can be passed in as the `depth` argument when the recursion should - for all intents and purposes - go as deep as is needed for the given set of values.

Let's create a deeply nested array and see what happens when we flatten it to different depths:

``````<cfscript>

data = [ "a", nullValue(), [ "b", [ "c", [ "d", [ "e", [ "f", [ "g", "i", nullValue() ] ] ] ] ] ] ];

dump(
var = flatten( data ),
label = "Depth: 1"
);
dump(
var = flatten( data, 2 ),
label = "Depth: 2"
);
dump(
var = flatten( data, 3 ),
label = "Depth: 3"
);
dump(
var = flatten( data, getMetadata( flatten ).MAX_DEPTH ),
label = "Depth: MAX_DEPTH"
);

// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //

/**
* I create a new array with all the sub-elements concatenated into it recursively up
* to the given depth. MAX_DEPTH metadata provided as a convenience for "Deep" flatten.
*/
public array function flatten(
required array values,
numeric depth = 1
)
MAX_DEPTH = 2147483647
{

var results = [];

for ( var value in values ) {

// If the array is sparse, skip over any null values during flattening.
if ( isNull( value ) ) {

continue;

}

// RECURSIVE CASE - if the given value is an array and we still have available
// depth to consume, recursively flatten the value and then MERGE its results
// into the current results.
if ( isArray( value ) && depth ) {

results.append( flatten( value, ( depth - 1 ) ), true );

// BASE CASE - end of recursion.
} else {

results.append( value );

}

}

return( results );

}

</cfscript>
``````

As you can see, the `flatten()` method will continue to call itself while there is available `depth`. And, each time the `flatten()` method is recursively called, the `depth` value is decremented. As such, it will eventually recurse down to `0`, fail the recursive test, enter the "base case", and halt recursion.

Test for the Reader: In the given implementation, what would happen if you passed `-1` is as the `depth` argument?

Now, when we run this ColdFusion code, we get the following output:

As you can see, as the `depth` argument is increased in value, the resultant array becomes increasingly flat. And, in the final invocation, when we pass-in `MAX_DEPTH` as the `depth` argument, the outcome is a completely flattened, one-dimensional array.

Recursion in fun! ColdFusion is awesome!

Want to use code from this post? Check out the license.

Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.