Skip to content

Instantly share code, notes, and snippets.

@DeveloperOfficeCom
Created July 19, 2025 13:09
Show Gist options
  • Select an option

  • Save DeveloperOfficeCom/4a0e2dd8ecea631c525ca412821cdb8e to your computer and use it in GitHub Desktop.

Select an option

Save DeveloperOfficeCom/4a0e2dd8ecea631c525ca412821cdb8e to your computer and use it in GitHub Desktop.
DefaultDict function for ColdFusion/Lucee - Struct with automatic default values
<!---
DEFAULTDICT Function
Description: Creates a struct wrapper that returns a default value for non-existent keys, similar to
Python's collections.defaultdict. When accessing a key that doesn't exist, it automatically creates
that key with the default value and returns it.
Parameters:
- defaultValue: The default value to return/set for missing keys (required)
- initialData: Optional struct to pre-populate the defaultdict (optional)
Returns: Struct with special behavior for missing keys
Usage:
<cfset dd = defaultdict(0)>
<cfset dd = defaultdict([])>
<cfset dd = defaultdict("N/A", {existingKey: "value"})>
Examples:
dd = defaultdict(0); dd.count++; // Works even though 'count' didn't exist
dd = defaultdict([]); arrayAppend(dd.items, "new"); // Auto-creates array
dd = defaultdict("unknown"); name = dd.name; // Returns "unknown"
Author: DeveloperOffice.com
Language: ColdFusion/Lucee
License: MIT (https://opensource.org/licenses/MIT)
Version: 1.0
--->
<cffunction name="defaultdict" returntype="struct" output="false">
<cfargument name="defaultValue" type="any" required="true">
<cfargument name="initialData" type="struct" required="false" default="#structNew()#">
<!--- Create the base struct with initial data --->
<cfset var baseStruct = duplicate(arguments.initialData)>
<!--- Store metadata about the default value --->
<cfset var metadata = {
defaultValue: arguments.defaultValue,
isDefaultValueCallable: false,
defaultType: ""
}>
<!--- Determine the type of default value --->
<cfif isSimpleValue(arguments.defaultValue)>
<cfset metadata.defaultType = "simple">
<cfelseif isArray(arguments.defaultValue)>
<cfset metadata.defaultType = "array">
<cfelseif isStruct(arguments.defaultValue)>
<cfset metadata.defaultType = "struct">
<cfelse>
<cfset metadata.defaultType = "other">
</cfif>
<!--- Create wrapper struct with onMissingMethod --->
<cfset var wrapper = {
_data = baseStruct,
_metadata = metadata,
<!--- Get method to retrieve values with default --->
get = function(key, customDefault) {
if (structKeyExists(this._data, arguments.key)) {
return this._data[arguments.key];
} else {
<!--- Use custom default if provided, otherwise use the defaultdict default --->
var valueToUse = "";
if (structKeyExists(arguments, "customDefault")) {
valueToUse = arguments.customDefault;
} else {
<!--- Create a copy of the default value to avoid reference issues --->
if (this._metadata.defaultType == "simple") {
valueToUse = this._metadata.defaultValue;
} else if (this._metadata.defaultType == "array") {
valueToUse = duplicate(this._metadata.defaultValue);
} else if (this._metadata.defaultType == "struct") {
valueToUse = duplicate(this._metadata.defaultValue);
} else {
valueToUse = this._metadata.defaultValue;
}
}
<!--- Set the key with the default value --->
this._data[arguments.key] = valueToUse;
return valueToUse;
}
},
<!--- Set method to set values --->
set = function(key, value) {
this._data[arguments.key] = arguments.value;
return arguments.value;
},
<!--- Check if key exists --->
has = function(key) {
return structKeyExists(this._data, arguments.key);
},
<!--- Delete a key --->
delete = function(key) {
if (structKeyExists(this._data, arguments.key)) {
structDelete(this._data, arguments.key);
return true;
}
return false;
},
<!--- Get all keys --->
keys = function() {
return structKeyArray(this._data);
},
<!--- Get all values --->
values = function() {
var result = [];
for (var key in this._data) {
arrayAppend(result, this._data[key]);
}
return result;
},
<!--- Get count of keys --->
count = function() {
return structCount(this._data);
},
<!--- Clear all data --->
clear = function() {
structClear(this._data);
},
<!--- Get the underlying data struct --->
getData = function() {
return duplicate(this._data);
},
<!--- Convert to regular struct --->
toStruct = function() {
return duplicate(this._data);
},
<!--- Handle dynamic property access --->
onMissingMethod = function(missingMethodName, missingMethodArguments) {
<!--- For property access like dd.someKey --->
if (arrayLen(arguments.missingMethodArguments) == 0) {
return this.get(arguments.missingMethodName);
} else {
<!--- For method calls or property setting --->
this.set(arguments.missingMethodName, arguments.missingMethodArguments[1]);
return arguments.missingMethodArguments[1];
}
}
}>
<cfreturn wrapper>
</cffunction>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment