Created
July 19, 2025 13:09
-
-
Save DeveloperOfficeCom/4a0e2dd8ecea631c525ca412821cdb8e to your computer and use it in GitHub Desktop.
DefaultDict function for ColdFusion/Lucee - Struct with automatic default values
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!--- | |
| 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