Wednesday, July 29, 2026

ColdFusion Structures - The 5 Minute Mission

You don't have hours to spend ramping up on ColdFusion.
You need to get in. Get out and deliver. Let's do that.

Copy and paste this into a cffiddle.org session.
Yep. Remember to wrap it with <cfscript> tags. Feel free to thank me in the comments.

// Tutorials with a SMART Mission
// What does that look like?

// SMART: Specific, Measurable, Achievable, Revelant and Time-bound
// S: Structure Functions
// M: Read and digest within 5 minutes
// A: Anyone new/familiar with programing can use them immediately
// R: Use a data in a common ways anyone will mentally and emotionally connnect with
// T: Read within 3 min, Research more within seconds and Experiment with immediately.
// ---------------------------------------------------------------------------------------

// ColdFusion structures are variables that have a "key" and "data" format
fruitStruct = {
    "A": "Apples", 
    "B": "Bananas", 
    "C": "Cantaloupe", 
    "G": "Grapes", 
    "K": "Kiwi", 
    "O": "Oranges", 
    "P": "Pomegranate",
    "S": "Strawberries", 
    "W": "Watermelon" 
};
meatStruct = {
    "B": "Beef", 
    "C": "Chicken", 
    "F": "Fish", 
		"L": "Lamb",
    "P": "Pork",
    "T": "Turkey"
};

missionIntroduction = "
    <p><b>ColdFusion Structure Functions</b><br>
    As a CF developer, you use these often. Pick these up. You gonna need all 6 of 'em.</p>

    <p><b>MISSION:</b> Operation Groceries. Go help your Mama with these two structures.</p>

    <p><b>VITAL DATA</b><br>
    <b>Fruits</b> (#structCount(fruitStruct)#): #serializeJSON(fruitStruct)#<br> 
    <b>Meat</b> (#structCount(meatStruct)#): #serializeJSON(meatStruct)#</p>

    <p><b>NEED TO KNOW</b><br>
		<b>Keys:</b> The keys of a structure are not case-sensitive by default.<br>
    In Adobe ColdFusion, you can define them as case-sensitive if you want to since CF 2021.<br>
    Lucee does not have this feature.  Sorry Charlie!  Now you know.</p>
";
writeOutput(missionIntroduction);

writeOutput("<b>EXAMPLES</b><br>");

// 1. structCount - Count items in Struct
totalFruits = structCount(fruitStruct);
writeOutput("1. <b>structCount(fruitStruct)</b><br>Total Fruit Count: #totalFruits#<br>");

// 2. structInsert - add to struct (a struct is not linear, it just a container)
structInsert(fruitStruct, "M", "Mangoes"); // Add Mangoes to the structure
writeOutput("2. <b>structInsert(fruitStruct, 'M', 'Mangoes')</b><br>" & serializeJSON(fruitStruct) & "<br>");

// What if the Key already exists?  structInsert will fail.  See structKeyExists function
try { 
  structInsert(fruitStruct, "M", "Monkfruit", false);
}
catch(any e) { 
  writeOutput("<br><em>ATTEMPT & FAIL</em>");
  writeOutput("<br>TASK: Try to insert a key that already exists");
	writeOutput(" - <b>structInsert(fruitStruct, 'M', 'Monkfruit', false)</b>");
	writeOutput("<br>ERROR INFO<br>Message: #e.message#<br>Detail: #e.detail#<br><br>");
}

// 3. structKeyExists - Check existence and if it does exist, show key's value
favoriteFruit = "Not yet found";
keyToFind = "K";  // Feel free to change this to "Q" to prove when Key does not exist
writeOutput("3. <b>structKeyExists(fruitStruct, #keyToFind#)</b>");
if (structKeyExists(fruitStruct, keyToFind)) {
    favoriteFruit = structFind(fruitStruct, keyToFind); // return value of key
    writeOutput(" & <b>structFind(fruitStruct, #keyToFind#)</b><br>")
		writeOutput("Key '#keyToFind#' exists! The value is: " & favoriteFruit & "<br>");
}
else{
  writeOutput(" & <b>structFind(fruitStruct, #keyToFind#)</b><br>");
	writeOutput("Key '#keyToFInd#' does NOT exist! The value is: " & favoriteFruit & "<br>");
}

// 4. structDelete - Remove key from Struct
deleteKeyW = structDelete(fruitStruct, "W"); // Removes Watermelon from the structure
writeOutput("4a. <b>structDelete(fruitStruct, 'W')</b> - #deleteKeyW# (did exist in Structure)<br>" & serializeJSON(fruitStruct) & "<br>");
// BONUS DISCOVERY: If you tried to delete a Key that did not exist, 
// you need the 3rd parameter "indicateNotExisting" to get an accurate boolean of false.  Strange but true.  Keep your boots laced.
deleteKeyQ = structDelete(fruitStruct, "Q", true);
writeOutput("4b.<b>structDelete(fruitStruct, 'Q')</b> -  Delete Key Q: #deleteKeyQ# (does not exist in Structure)<br>");

// 5. structAppend - Append one structure to another one
// Create a separate copy to show appending without destroying our original fruitStruct
appendFruityMeatStruct = duplicate(fruitStruct);

// Merges meatStruct into appendDemoStruct. 
structAppend(appendFruityMeatStruct, meatStruct, false); // False = will NOT overwrite matching keys (B, C, P)
writeOutput("5. <b>structAppend(fruitStruct, meatStruct, false)</b><br>");
writeOutput("<em>(Nope!  Pork, Beef and Chicken didn't make the cut. A key already existed. Remember: Override = false)</em><br>");
writeOutput(serializeJSON(appendFruityMeatStruct) & "<br>");

// Include any additional information that helps summarizes the above examples
additionalPoints = "<br><hr>
<p>
 <b>Additional Points:</b>
 <ul>
  <li><b>Keys are Not Case-sensitive:</b> This is by default.  It could be you assign new data to an existing key and you don't see it updated.  If you wanted it to -- use the Overwrite flag Luke.  I am your Father.</li>
	<li><b>Nested Structures</b> Yeah, it's possible.  We didn't talk about that here, but yeah.  Nesting can quickly become more challenging.  Don't fear it.  Embrace it.</li>
	<li><b>Structures Under the Hood:</b> Structures may have a count of total items, but you cannot use a formal indexed for loop.  Loops for Structures on the next tutorial.</li>
 </ul>
 </p>";
writeOutput(additionalPoints);

Let me know if I've missed your favorite structure function. Or better yet drop a comment so everyone can learn about a brave/bold structure function from ColdFusion.

MORE INPUT
After you've shot your full mag at this code, go have a look at ColdFusion documentation at Adobe and Foundeo's CFDocs.

Thursday, July 23, 2026

ColdFusion Loops - The 5 Minute Mission

You don't have hours to spend ramping up on ColdFusion.
You need to get in. Get out and deliver. Let's do that.

Copy and paste this into a cffiddle.org session.
Yep. Remember to wrap it with <cfscript> tags. Feel free to thank me in the comments.

// ---------------------------------------------------------------------------------------
// Tutorials with a SMART Mission
// What does that look like?

// SMART: Specific, Measurable, Achievable, Revelant and Time-bound
// S: Loop over strings, lists and arrays
// M: Read and digest within 5 minutes
// A: Anyone new/familiar with programing can use them immediately
// R: Use a data in a common ways anyone will mentally and emotionally connnect with
// T: Read within 3 min, Research more within seconds and Experiment with immediately.
// ---------------------------------------------------------------------------------------

// String is a variable is made up of characters.
missionString = "GROCERIES";

// Lists are strings with a delimiter
fruitList = "Apples,Bananas,Blueberries,Strawberries,Grapes,Oranges,Cantaloupe,Watermelon,Kiwi,Pomegranate";
veggieList = "Spinach,Kale,Broccoli,Cauliflower,Radish,Carrot,Potato,Pepper,Corn,Onion";
meatList = "Chicken,Turkey,Fish,Beef,Pork";

// ColdFusion arrays are variables like a giant wall of shelves. You can keep your stuff there.
fruitArray = ["Apples", "Bananas", "Blueberries", "Strawberries", "Grapes", "Oranges", "Cantaloupe", "Watermelon", "Kiwi", "Pomegranate"];
veggieArray = ["Spinach", "Kale", "Broccoli", "Cauliflower", "Radish", "Carrot", "Potato", "Pepper", "Corn", "Onion"];
meatArray = ["Chicken", "Turkey", "Fish", "Beef", "Pork"];

missionIntroduction = "
    <p><b>ColdFusion Loops</b><br>
    As a CF developer, your variables are your variables. Love them.<br>
    Say it.  They are mine. There are many like them. But these are mine.</p>

    <p><b>MISSION:</b> Operation Groceries. Go help your Mama with these variables.</p>

    <p><b>VITAL DATA</b><br>
    <b>missionString</b> : #missionString# (#len(missionString)# characters in length)<br><br>

    <b>Fruits</b> (#arrayLen(fruitArray)#): #ArrayToList(fruitArray)#<br>
    <b>Veggies</b> (#arrayLen(veggieArray)#): #ArrayToList(veggieArray)#<br>
    <b>Meat</b> (#arrayLen(meatArray)#): #ArrayToList(meatArray)#</p>


    <p><b>NEED TO KNOW</b><br><b>1-Based Indexing:</b> ColdFusion lists start at index 1.<br>
    Yes, they start at 1. What the heck? It's ColdFusion's thing.<br>
    Call it a quirk. Get used to it. Love the number 1 and like it.<br>
    Other languages have their indexes start at 0 like JavaScript, C++, Java or Python.</p>
";
writeOutput(missionIntroduction);

writeOutput("<b>EXAMPLES</b><br>");
// 1. Loop through strings
writeOutput("1. Loop through strings (one character at a time):<br>");
for (i = 1; i <= len(missionString); i++) {
    char = mid(missionString, i, 1); // Get single character from string
    writeOutput(char & " ");
}
writeOutput("<br>"); // outputs: G R O C E R I E S

// 2a: Loop through fruitList with for-in
writeOutput("<p>2a. Loop through Fruit lists (one item at a time) - For-In<br>");
writeOutput("<strong>Fruit List:</strong><br>");
for (item in fruitList) {
    writeOutput(item & "<br>");
}
writeOutput("</p>");  // outputs each fruit on a new line

// 2b: Loop through meatList with index-based for
writeOutput("<p>2b. Loop through Meatlists (one item at a time) - Index-Based for<br>");
writeOutput("<strong>Meat List:</strong><br>");
for (i = 1; i <= listLen(meatList); i++) {
    item = listGetAt(meatList, i);
    writeOutput(i & ": " & item & "<br>");
}
writeOutput("</p>"); // outputs each meat with its index on a new line

// 3a: Loop through veggieArray with for-in
writeOutput("<p>3a. Loop through Veggie arrays (one item at a time) - For-In<br>");
writeOutput("<strong>Veggie Array:</strong><br>");
for (item in veggieArray) {
    writeOutput(item & "<br>");
}
writeOutput("</p>");  // outputs each veggie on a new line

// 3b: Loop through fruitArray with index-based for
writeOutput("<p>3b. Loop through fruitArray (one item at a time) - Index-based for:<br>");
writeOutput("<strong>Fruit Array:</strong><br>");
for (i = 1; i <= arrayLen(fruitArray); i++) {
    writeOutput(i & ": " & fruitArray[i] & "<br>");
}
writeOutput("</p>"); // outputs each fruit with its index on a new line

// Include any additional information that helps summarizes the above examples
additionalPoints = "<br><hr>
<p>
 <strong>Additional Points:</strong>
 <ul>
  <li><b>Loops Under the Hood:</b> Loops can start any where but if you loop through strings, lists and arrays they start at 1.  Yep!  Index-based at 1.</li>
  <li><b>Empty Lists or Arrays:</b> It happens. If a list or array has no items, it won't enter the loop.  It won't do jack-squat.  There's nothing to loop over.  Keep that in mind.</li>
  <li><b>Looping Over Structs:</b> We haven't talked about those yet.  They are the grub, you've been waiting for bub.  You need those.  But that's a whole other tutorial.</li>
 </ul>
 </p>";
writeOutput(additionalPoints);

Let me know if I've missed your favorite loop. Or better yet point me to another site where everyone can learn about a loop from ColdFusion.

MORE INPUT
After your done with your morning run at this code, go have a look at ColdFusion documentation at Adobe and Foundeo's CFDocs.

Wednesday, July 22, 2026

ColdFusion Array Functions - The 5 Minute Mission

You don't have hours to spend ramping up on ColdFusion.
You need to get in. Get out and deliver. Let's do that.

Copy and paste this into a cffiddle.org session.
Yep. Remember to wrap it with <cfscript> tags. Feel free to thank me in the comments.

// ---------------------------------------------------------------------------------------
// Tutorials with a SMART Mission
// What does that look like?

// SMART: Specific, Measurable, Achievable, Revelant and Time-bound
// S: Array Functions
// M: Read and digest within 5 minutes
// A: Anyone new/familiar with programing can use them immediately
// R: Use a data in a common ways anyone will mentally and emotionally connnect with
// T: Read within 3 min, Research more within seconds and Experiment with immediately.
// ---------------------------------------------------------------------------------------

// ColdFusion arrays are variables like a giant wall of shelves. You can keep your stuff there.
fruitArray = ["Apples", "Bananas", "Blueberries", "Strawberries", "Grapes", "Oranges", "Cantaloupe", "Watermelon", "Kiwi", "Pomegranate"];
veggieArray = ["Spinach", "Kale", "Broccoli", "Cauliflower", "Radish", "Carrot", "Potato", "Pepper", "Corn", "Onion"];
meatArray = ["Chicken", "Turkey", "Fish", "Beef", "Pork"];

missionIntroduction = "
    <p><b>ColdFusion Array Functions</b><br>
    As a CF developer, you use these on a daily basis. Pick these up. You gonna need all 11 of 'em.</p>

    <p><b>MISSION:</b> Operation Groceries. Go help your Mama with these three arrays.</p>

    <p><b>VITAL DATA</b><br>
    <b>Fruits</b> (#arrayLen(fruitArray)#): #ArrayToList(fruitArray)#<br>
    <b>Veggies</b> (#arrayLen(veggieArray)#): #ArrayToList(veggieArray)#<br>
    <b>Meat</b> (#arrayLen(meatArray)#): #ArrayToList(meatArray)#</p>

    <p><b>NEED TO KNOW</b><br><b>1-Based Indexing:</b> ColdFusion lists start at index 1.<br>
    Yes, they start at 1. What the heck? It's ColdFusion's thing.<br>
    Call it a quirk. Get used to it. Love the number 1 and like it.<br>
    Other languages have their indexes start at 0 like JavaScript, C++, Java or Python.</p>
";
writeOutput(missionIntroduction);

writeOutput("<b>EXAMPLES</b><br>");
// 1. arrayLen - Count items in fruit Array
totalFruits = arrayLen(fruitArray);
writeOutput("1. arrayLen(fruitArray) - Total fruits: #totalFruits#<br>");
// Output: 10

// 2. arrayAppend - add to end
ArrayAppend(fruitArray, "Mango");
WriteOutput("2. arrayAppend(fruitArray, 'Mango') - " & ArrayToList(fruitArray) & "<br>");
// ["Apples", "Bananas", "Blueberries", "Strawberries", "Grapes", "Oranges", "Cantaloupe", "Watermelon", "Kiwi", "Pomegranate", "Mango"]

// 2. arrayPrepend: add to start
ArrayPrepend(veggieArray, "Garlic");
WriteOutput("3. arrayPrepend(veggieArray, 'Garlic') - " & ArrayToList(veggieArray) & "<br>");
// ["Garlic", "Spinach", "Kale", "Broccoli", "Cauliflower", "Radish", "Carrot", "Potato", "Pepper", "Corn", "Onion"]

// 4. arrayInsertAt: Insert at specific index (shifts others down)
ArrayInsertAt(meatArray, 2, "Lamb"); // Insert "Lamb" at index 2 in meatArray
WriteOutput("4. arrayInsertAt(meatArray, 2, 'Lamb') - " & ArrayToList(meatArray) & "<br>");
// ["Chicken", "Lamb", "Turkey", "Fish", "Beef", "Pork"]

// 5. arrayDeleteAt: Remove item at index 5 (Oranges)
ArrayDeleteAt(fruitArray, 5);
WriteOutput("5. ArrayDeleteAt(fruitArray, 5) - " & ListToArray(fruitArray) & "<br>");
// ["Apples", "Bananas", "Blueberries", "Strawberries", "Oranges", "Cantaloupe", "Watermelon", "Kiwi", "Pomegranate", "Mango"]

// 6. arrayFind: Case-sensitive (returns index or 0 if not found)
pos = ArrayFind(fruitArray, "kiwi"); 
WriteOutput("6. ArrayFind(fruitArray, 'kiwi') - " & pos & " (0 = not found)<br>");
// Expected: 0 (not found)

// 7. arrayFindNoCase: Case-insensitive search (returns index or 0 if not found)
posNoCase = ArrayFindNoCase(fruitArray, "kiwi");
WriteOutput("7. arrayFindNoCase(fruitArray, 'kiwi') - " & posNoCase & " (9 = found)<br>");
// Expected: 9 (or current index)

// 8. arrayContains: Returns Boolean if array contains value
hasBroccoli = ArrayContains(veggieArray, "Broccoli");
WriteOutput("8a. arrayContains(veggieArray, 'Broccoli') - " & hasBroccoli & "<br>");
// Expected: true

hasRice = ArrayContains(veggieArray, "Rice");
WriteOutput("8b. arrayContains(veggieArray, 'Rice') - " & hasRice & "<br>");
// Expected: false

// 9. arraySlice: Extract subset (Start Index, Length)
firstThreeVeggies = ArraySlice(veggieArray, 1, 3);
WriteOutput("9. arraySlice(veggieArray, 1, 3) - " & ArrayToList(firstThreeVeggies) & "<br>");
// Expected: Garlic, Spinach, Kale

// 10. ArrayToList: Convert to string
meatCSV = ArrayToList(meatArray);
WriteOutput("10. arrayToList(meatArray) - " & meatCSV & "<br>");
// Output: Chicken,Lamb,Turkey,Fish,Beef,Pork

// ArrayToList with custom delimiter
fruitPipe = ArrayToList(fruitArray, "|");
WriteOutput("11. arrayToList(fruitArray, '|') - " & fruitPipe & "<br>");
// Output: Apples|Bananas|Blueberries|Strawberries|Oranges|Cantaloupe|Watermelon|Kiwi|Pomegranate|Mango

// Include any additional information that helps summarizes the above examples
additionalPoints = "<br><hr>
<p>
 <strong>Additional Points:</strong>
 <ul>
  <li><b>Arrays Under the Hood:</b> ArrayAppend alters the original variable; yeah, it's cool. Keep the change.</li>
  <li><b>Find vs. Contains:</b> ArrayFind expects an exact match of the whole list item, but ArrayContains matches partial substrings inside an item.  They search differently.  Shove that in your cargo pant pocket.</li>
 </ul>
 </p>";
writeOutput(additionalPoints);

Let me know if I've missed your favorite array function. Or better yet point me to another site where everyone can learn about a hearty array function from ColdFusion.

MORE INPUT
After you've shot your full mag at this code, go have a look at ColdFusion documentation at Adobe and Foundeo's CFDocs.

Monday, July 20, 2026

ColdFusion String Functions - The 5 Minute Mission

You don't have hours to spend ramping up on ColdFusion.
You need to get in. Get out and deliver. Let's do that.

Copy and paste this into a cffiddle.org session.
Yep. Remember to wrap it with <cfscript> tags. Feel free to thank me in the comments.

// ---------------------------------------------------------------------------------------
// Tutorials with a SMART Mission
// What does that look like?
// 
// SMART: Specific, Measurable, Achievable, Revelant and Time-bound
// S: String Functions
// M: Read and digest within 5 minutes
// A: Anyone new/familiar with programing can use them immediately
// R: Use a data in a common ways anyone will mentally and emotionally connnect with
// T: Read within 3 min, Research more within seconds and Experiment with immediately.
// ---------------------------------------------------------------------------------------

// ColdFusion strings are variables made up of characters (letters, numbers, etc.)

// 1. Define the initial data sets
fruitList  = "Apples,Bananas,Blueberries,Strawberries,Grapes,Oranges,Cantaloupe,Watermelon,Kiwi,Pomegranate";
veggieList = "Spinach,Kale,Broccoli,Cauliflower,Radish,Carrot,Potato,Pepper,Corn,Onion";
meatList   = "Chicken,Turkey,Fish,Beef,Pork";

missionIntroduction = "
    <p><b>ColdFusion String Functions</b><br>
    As a CF developer, you use these often. Pick these up. You gonna need all 11 of 'em.</p>

    <p><b>MISSION:</b> Operation Groceries. Go help your Mama because she's a remarkable lady.</p>

    <p><b>VITAL DATA</b><br>
    <b>Fruits</b> (#listLen(fruitList)#): #fruitList#<br>
    <b>Veggies</b> (#listLen(veggieList)#): #veggieList#<br>
    <b>Meat</b> (#listLen(meatList)#): #meatList#</p>

    <p><b>NEED TO KNOW</b><br><b>1-Based Indexing:</b> ColdFusion strings start at index 1.<br>
    Yes, they start at 1. What the heck? It's ColdFusion's thing.<br>
    Call it a quirk. Get used to it. Love the number 1 and like it.<br>
    Other languages have their indexes start at 0 like JavaScript, C++, Java or Python.</p>
";
writeOutput(missionIntroduction);

writeOutput("<b>EXAMPLES</b><br>");

// 1. Execute String Length
lenResult = Len(meatList); 
writeOutput("1. Len(meatList) - #lenResult#  (that's characters to you, soldier)<br><br>");

// 2. Trim string of leading and trailing spaces
paddedMeat  = "  " & meatList & "  ";
writeOutput("<em>paddedMeat has spaces:</em> '#paddedMeat#'<br>");
trimResult  = Trim(paddedMeat); 
writeOutput("2.Trim(paddedMeat) - '#trimResult#' (trimmed leading/trailing spaces)<br><br>");

// 3. Execute conversion to Uppercase and Lowercase
ucResult = UCase(meatList);
writeOutput("3a. UCase(meatList) - #ucResult#<br>");

lcResult = LCase(veggieList); 
writeOutput("3b. LCase(veggieList) - #lcResult#<br><br>");

// 4. Execute extraction of substrings
leftResult = Left(fruitList, 6); // Apple 
writeOutput("4a. Left(fruitList, 6) - #leftResult#<br>");

rightResult = Right(veggieList, 5); // Onion
writeOutput("4b. Right(veggieList, 5) - #rightResult#<br>");

midResult = Mid(meatList, 9, 6);  // Turkey
writeOutput("4c. Mid(meatList, 9, 6) - #midResult#<br><br>");


// 5. Execute Finding and Searching (1-based index results. Found = index, Not Found = 0)
// Case-sensitive search...
findFailResult = Find("beef", meatList); 
writeOutput("5a. Find('beef') - #findFailResult# (not found; case-sensitive search)<br>");

findResult = Find("Beef", meatList);
writeOutput("5b. Find('Beef') - #findResult# (found at index 21; case-sensitive search) <br>");

// Non-case-sensitive search...
findNoCaseResult = FindNoCase("beef", meatList);
// IMHO: Everyone should always capitalize Beef anyway, because it's the best meat on the planet.
writeOutput("5c. FindNoCase('beef') - #findNoCaseResult# (found at index 21 - NOT case-sensitive) <br><br>");

// 6. Execute String Modifications (Insert uses 0-based index)
replaceResult = Replace(fruitList, "Strawberries", "Blackberries"); 
writeOutput("6a. Replace(fruitList, 'Strawberries', 'Blackberries')<br> #replaceResult#<br>");
insertResult = Insert("Lamb,", meatList, 8); 
writeOutput("6b. Insert('Lamb,' meatList, 8) - #insertResult#<br>");

// Include any additional information that helps summarizes the above examples
additionalPoints = "<br><hr>
<p>
 <strong>Additional Points:</strong>
 <ul>
  <li><b>Strings Under the Hood:</b> Long strings can impact on performance.  Keep 'em short.  Too long? Then look into Arrays.</li>
  <li><b>Insert, but no Remove:</b> Correct.  If you want remove a chunk of a string you would use replace.</li>
  <li><b>Missed GetToken:</b> Good catch!  I did that on purpose.  That will be discussed in the next post on Arrays.</li>
 </ul>
 </p>";
writeOutput(additionalPoints);

MORE INPUT
After you've bench pressed this code, go have a look at ColdFusion documentation at Adobe and Foundeo's CFDocs. Then read about the string performance article by Brad Wood Confessions of a Speed Junky: How I made my code faster.

Tuesday, July 14, 2026

List Functions Game - Guess What is Missing

You need to use the List Functions from our last post. Here's a game.

Tweak it. Devour it. Then write your own.

Copy and paste this into a cffiddle.org session.
Yep. Remember to wrap it with <cfscript> tags. Feel free to thank me later.


// ---------------------------------------------------------------------------------------
// GAME with a SMART Mission
// What does that look like?
//
// SMART: Specific, Measurable, Achievable, Revelant and Time-bound
// S: List Functions - Guess What is Missing from the List
// M: Read and digest within 5 minutes
// A: Anyone new/familiar with programing can use them immediately
// R: Use a data in a common ways anyone will mentally and emotionally connnect with
// T: Read within 3 min, Research more within seconds and Experiment with immediately.
// ---------------------------------------------------------------------------------------
// FUNCTIONS USED:
//  List: listLen, listGetAt, listDeleteAt
//  String: trim
// ---------------------------------------------------------------------------------------

// Add a CSS class to render the "Answer" to the bottom of the page
// ** This isn't a best practice, soldier.  
// ** Be a pro, put styles in an external stylesheet.
writeOutput("<style>.pushDownLower { margin-top: 22em; }</style>");

// Initialize the list.  You need your protein and it better be free-range and grass-fed.
originalList = "Beef, Chicken, Fish, Lamb, Pork, Turkey";

listLength = originalList.listLen();

// Pick a random index to delete (between 1 and total items)
randomIndex = RandRange(1, listLength);

// Get the exact item being removed for the game log
removedItem = originalList.listGetAt(randomIndex);

// Remove the random item from the list
updatedList = originalList.listDeleteAt(randomIndex);

// You'll need a humorous retort later.
// No one likes stuff taken away from them.
snappyComeBackArray = [
 "Moo! Moo! I like my shoes.  Give them back.",
 "I like my BBQ with sweet potatoes and deep-fried.",
 "Fresh sushi is calling my name, even now.",
 "Get in my belly with caramelized onions.",
 "Everything goes better with bacon.",
 "Oh how thankful I am.  I am."
];

// Capture the output inside a variable using savecontent
savecontent variable="gameOutput" {
  writeOutput("
    <h2>Guess What is Missing...</h2>
    <p><b>Original:</b> #originalList# (#listLength# items)</p>
    <p><b>Updated:</b> #updatedList#</p>
    <p class='pushDownLower'><hr>Randomly removed item <em>#randomIndex# (#removedItem.trim()#)...</em></p>
    <h1>#snappyComeBackArray[randomIndex]#</h1>
  ");
}

// Display the captured content to the screen
writeOutput(gameOutput);

If you have another game in mind, let me know in the comments.

Sunday, July 12, 2026

ColdFusion List Functions - The 5 Minute Mission

You don't have hours to spend ramping up on ColdFusion.
You need to get in. Get out and deliver. Let's do that.

Copy and paste this into a cffiddle.org session.
Yep. Remember to wrap it with <cfscript> tags. Feel free to thank me in the comments.


// ---------------------------------------------------------------------------------------
// Tutorials with a SMART Mission
// What does that look like?

// SMART: Specific, Measurable, Achievable, Revelant and Time-bound
// S: List Functions
// M: Read and digest within 5 minutes
// A: Anyone new/familiar with programing can use them immediately
// R: Use a data in a common ways anyone will mentally and emotionally connnect with
// T: Read within 3 min, Research more within seconds and Experiment with immediately.
// ---------------------------------------------------------------------------------------

// ColdFusion lists are strings separated by a delimiter (default is a comma)
fruitList = "Apples,Bananas,Blueberries,Strawberries,Grapes,Oranges,Cantaloupe,Watermelon,Kiwi,Pomegranate";
veggieList = "Spinach,Kale,Broccoli,Cauliflower,Radish,Carrot,Potato,Pepper,Corn,Onion";
meatList = "Chicken,Turkey,Fish,Beef,Pork";

missionIntroduction = "
    <p><b>ColdFusion List Functions</b><br>
    As a CF developer, you use these on a daily basis. Pick these up. You gonna need all 11 of 'em.</p>

    <p><b>MISSION:</b> Operation Groceries. Go help your Mama with these three lists.</p>

    <p><b>VITAL DATA</b><br>
    <b>Fruits</b> (#listLen(fruitList)#): #fruitList#<br>
    <b>Veggies</b> (#listLen(veggieList)#): #veggieList#<br>
    <b>Meat</b> (#listLen(meatList)#): #meatList#</p>

    <p><b>NEED TO KNOW</b><br><b>1-Based Indexing:</b> ColdFusion lists start at index 1.<br>
    Yes, they start at 1. What the heck? It's ColdFusion's thing.<br>
    Call it a quirk. Get used to it. Love the number 1 and like it.<br>
    Other languages have their indexes start at 0 like JavaScript, C++, Java or Python.</p>
";
writeOutput(missionIntroduction);

writeOutput("<b>EXAMPLES</b><br>");
// 1. ListLen - Count the items in the fruit list
totalFruits = ListLen(fruitList);
writeOutput("1. <b>ListLen(fruitList)</b> - Total fruits: #totalFruits#<br>");
// Output: 10

// 2. ListGetAt - Fetch a specific item by its 1-based index
thirdFruit = ListGetAt(fruitList, 3);
writeOutput("2. <b>ListGetAt(fruitList, 3)</b> - 3rd fruit: #thirdFruit#<br>");
// Output: Blueberries

// 3. ListFindNoCase - Search for an item ignoring capital letters
veggieIndexNoCase = ListFindNoCase(veggieList, "potATO");
writeOutput("3. <b>ListFindNoCase(veggieList, 'potATO')</b> - 'potATO' found at position: #veggieIndexNoCase# <em>(not case-sensitive)</em><br>");
// Output: 7

// 4. ListContains - Search for an item that *contains* a partial string
// 'flower' is part of 'Cauliflower'
partialSearchIndex = ListContains(veggieList, "flower");
writeOutput("4. <b>ListContains(veggieList, 'flower')</b> - Element containing 'flower' (Cauliflower) at position: #partialSearchIndex#<br>");
// Output: 4

// 5. ListFind - Search for an item matching exact case sensitivity
veggieIndexCase = ListFind(veggieList, "potato"); 
writeOutput("5. <b>ListFind(veggieList, 'potato')</b> - Exact match 'potato' position (0 means not found): #veggieIndexCase# <em>(case-sensitive)</em><br>");
// Output: 0 (it is capitalized as 'Potato')

// 6. ListAppend - Add meat list to the vegetable list to make a dinner list
dinnerList = ListAppend(veggieList, meatList);
writeOutput("6. <b>ListAppend(veggieList, meatList)</b> - Combined Veggie + Meat list: #dinnerList#<br>");
// both veggieList and meatList all together

// 7. ListPrepend - Add lamb list to the meat list
meatList = ListPrepend(meatList, "Lamb");
writeOutput("7. <b>ListPrepend(meatList, 'Lamb') </b> - #meatList#<br>");
// Output: Lamb is in the first index

// 8. ListFirst - Grab the very first item from the combined dinner list
firstDinnerItem = ListFirst(dinnerList);
writeOutput("8. <b>ListFirst(dinnerList)</b> - First item to cook: #firstDinnerItem#<br>");
// Output: Spinach

// 9. ListLast - Grab the very last item from the combined dinner list
lastDinnerItem = ListLast(dinnerList);
writeOutput("9. <b>ListLast(dinnerList)</b> - Last item to cook: #lastDinnerItem#<br>");
// Output: Pork

// 10. ListDeleteAt - Remove the 2nd element (Kale) from the vegetable list
shortVeggieList = ListDeleteAt(veggieList, 2);
writeOutput("10. <b>ListDeleteAt(veggieList, 2)</b> - Veggies after removing index 2: #shortVeggieList#<br>");

// 11. ListChangeDelims - Swap the commas for pipes (|) to change data formatting
pipeDelimitedMeat = ListChangeDelims(meatList, "|");
writeOutput("11. <b>ListChangeDelims(meatList, '|')</b> - Meat list with new delimiters: #pipeDelimitedMeat#<br>");

// Include any additional information that helps summarizes the above examples
additionalPoints = "<br><hr>
<p>
 <strong>Additional Points:</strong>
 <ul>
  <li><b>Strings Under the Hood:</b> ListAppend does not alter the original variable dynamically; you must assign it back to a variable (e.g., dinnerList = ListAppend(...)).</li>
  <li><b>Find vs. Contains:</b> ListFind expects an exact match of the whole list item, but ListContains matches partial substrings inside an item.  They search differently.</li>
 </ul>
 </p>";
writeOutput(additionalPoints);

Let me know if I've missed your favorite list function. Or better yet point me to another article where everyone can learn about about a nifty List function from ColdFusion.

MORE INPUT
Check out CF Foundations: Leveraging the List Built-In Functions by Mark Takata on YouTube.

Sunday, February 20, 2011

ColdFusion: Flatten an XML object to a Struct

On a recent ColdFusion project I had to make a PDF file respond like a traditional web page. You know, add form fields with a submit button, capture the submitted data server-side, and show the new PDF with the data submitted by the user. Even though this Adobe technology has been around a while and I knew it was possible, I had never done it before. To accomplish this, I used several tools:

  • Adobe Professional - To add form fields and JavaScript validation
  • IText - To populate form fields and flatten the PDF

So began a new coding adventure and, again, I found myself intrigued by this software challenge. I created a simple PDF, added JavaScript validation via Adobe Professional, and then began playing around with how submission works. I discovered a few differences from a traditional HTML page, but that's another post.

I set the PDF form submit options to submit form field data as XML. I quickly discovered I had to "loop through" the PDF form fields in an XML object. However, I wanted the XML data converted into a ColdFusion structure.

Q: Surely someone has already written a function like this right?
A: Nope. Or at least I didn't find anyone doing it.

Fortunately, I did find something that helped me refactor a solution. On www.cflib.org I found a flattenStruct function. It recursively traversed through a structure and created a new structure, a flatten structure.

From this, I created a new function, flattenXmlToStruct.

<cffunction name="flattenXmlToStruct" access="public" output="false" returntype="struct">
<cfargument name="xmlObject" required="true" type="xml" />
<cfargument name="delimiter" required="false" type="string" default="." />
<cfargument name="prefix" required="false" type="string" default="" />
<cfargument name="stResult" required="false" type="struct" />
<cfargument name="addPrefix" required="false" type="boolean" default="true" />

<cfset var sKey = '' />
<cfif NOT isDefined("arguments.stResult")>
 <cfset arguments.stResult = structNew()>
</cfif>

<cfloop from="1" to="#ArrayLen(arguments.xmlObject.XmlChildren)#" index="arrIndx">

 <cfset sKey = arguments.xmlObject.XmlChildren[arrIndx].XmlName>

 <cfif ArrayLen(arguments.xmlObject.XmlChildren[arrIndx].XmlChildren) EQ 0>
   <cfif arguments.addPrefix and len(arguments.prefix)>
     <cfset arguments.stResult["#arguments.prefix##arguments.delimiter##sKey#"] =
                          arguments.xmlObject.XmlChildren[arrIndx].XmlText />
   <cfelse>
     <cfset arguments.stResult[sKey] =
                          arguments.xmlObject.XmlChildren[arrIndx].XmlText />
 </cfif>

 <cfelse> <!--- Node has more children... --->
   <cfif arguments.prefix EQ "">
     <cfset currentKey = sKey>
   <cfelse>
     <cfset currentKey = "#arguments.prefix##arguments.delimiter##sKey#">
   </cfif>

   <cfset flattenXmlToStruct(arguments.xmlObject.XmlChildren[arrIndx],
                                arguments.delimiter, currentKey, arguments.stResult) />

 </cfif>
</cfloop>

<cfreturn arguments.stResult />
</cffunction>