Null Values in Structs and Arrays
Null Values in Structs and Arrays
Structs and arrays both can hold null values, but iteration visits them differently. Knowing that difference avoids "variable does not exist" / "value is NULL" errors and makes copy or transform code simpler.
This applies to both <cfloop> (loop struct / loop array) and script for ( ... in ... ) loops — the null visit/skip rules are the same.
For partial vs full null support, IsNull(), and StructKeyExists() behaviour, see Null Handling in CFML.
Structs: null values are visited
When a struct key is set to null, iteration still yields that key. The value is null, so you must guard before using it.
loop struct
sct = {};
sct[ "one" ] = "one";
sct[ "two" ] = nullValue();
sct[ "three" ] = "three";
loop struct=sct index="k" item="v" {
echo( k & ":" & ( v ?: "null" ) & ";" );
}
// Example output (order may vary for unordered structs):
// two:null;three:three;one:one;
for ( key in struct )
Same visit behaviour — null values are not skipped:
sct = {};
sct[ "one" ] = "one";
sct[ "two" ] = nullValue();
sct[ "three" ] = "three";
for ( k in sct ) {
v = sct[ k ];
echo( k & ":" & ( isNull( v ) ? "null" : v ) & ";" );
}
// Example output (order may vary for unordered structs):
// two:null;three:three;one:one;
Direct bracket or member access to a null struct value can throw under partial null support ("the value from key [...] is NULL"). Prefer the loop item from loop struct, or check with IsNull() before reading:
if ( isNull( sct[ "two" ] ) ) {
// safe: key present with null, or not present, depending on null support
}
Arrays: null / undefined slots are skipped
For arrays, both loop array and for ( item in array ) only visit defined, non-null indexes. Explicit nulls and sparse gaps are skipped — there is no need for an isNull( v ) check inside the loop body for those missing slots.
loop array
arr = [];
arr[ 1 ] = "one";
arr[ 2 ] = nullValue();
arr[ 3 ] = "three";
loop array=arr index="k" item="v" {
echo( k & ":" & v & ";" );
}
// Output:
// 1:one;3:three;
for ( item in array )
Same skip behaviour — null / undefined slots are not visited:
arr = [];
arr[ 1 ] = "one";
arr[ 2 ] = nullValue();
arr[ 3 ] = "three";
for ( v in arr ) {
echo( v & ";" );
}
// Output:
// one;three;
Sparse arrays behave the same way: only existing indexes appear.
arr = [];
arr[ 1 ] = "one";
arr[ 3 ] = "three";
loop array=arr index="k" item="v" {
echo( k & ":" & v & ";" );
}
// Output:
// 1:one;3:three;
Use ArrayIsDefined() when you need to know whether a specific index exists.
Best practice: prefer loop when you need the index
Null visit/skip rules are the same for loop and for-in. Prefer <cfloop> with index and item when you need the key or array index (for example to rebuild a sparse array).
| Collection | Null behaviour | Typical guard |
|---|---|---|
Struct (loop struct or for-in) |
Null values are visited | if ( isNull( v ) ) |
Array (loop array or for-in) |
Null / undefined slots are skipped | Usually none |
Copy or transform pattern
Assign by the loop index/key so sparse array shapes stay intact. Handle null only for structs:
function copyCollection( required any input ) {
if ( isStruct( arguments.input ) ) {
var result = [:];
loop struct=arguments.input index="k" item="v" {
if ( isNull( v ) ) {
result[ k ] = nullValue();
}
else {
result[ k ] = v; // or recurse / transform
}
}
return result;
}
if ( isArray( arguments.input ) ) {
var result = [];
loop array=arguments.input index="k" item="v" {
// null / undefined slots are not visited
result[ k ] = v; // or recurse / transform
}
return result;
}
return arguments.input;
}
Summary
- Struct nulls are visited by both
loop structandfor-in— checkisNull( v )(orv ?: default) before use. - Array nulls and gaps are skipped by both
loop arrayandfor-in— keep the loop body simple. - Use
loop struct/loop arraywithindexanditemwhen you need keys or indexes (for exampleresult[ k ] = ...to preserve sparse arrays).