The for loop from earlier in this module works by manually managing an index — you create a counter, check it against a limit, and remember to update it every pass. Most of the time, when you're looping through an array, struct, list, or query, you don't actually care about the index itself — you just want each item.
CFScript's for...in loop (sometimes called a "foreach" loop in other languages) hands you each item directly, without a counter to manage. ColdFusion's <cfloop> tag has matching typed variants for each data structure.
Learning Objectives
After completing this lesson, you'll be able to:
- Loop over an array with for...in.
- Loop over a struct's keys with for...in.
- Loop over a list with for...in.
- Loop over query rows with for...in.
- Use the matching <cfloop> tag-syntax variant for each data type.
Looping Over an Array
No index counter to declare, check, or update — fruit is handed to you directly each pass.
Looping Over a Struct
For a struct, for...in gives you each key — use it to look up the matching value.
Struct keys come back in an unpredictable order in both CFScript and tag syntax — don't rely on the order matching how you defined them.
Looping Over a List
Looping Over Query Rows
for...in over a query hands you the current row as a struct-like object. query.currentRow gives you the row number if you need it.
Query loops are common enough to deserve their own deep dive — the next lesson (<cfloop>) covers additional options like grouping.
Real-World Example: Building an HTML List
Common Beginner Mistakes
Expecting struct key order to match insertion order
for (key in myStruct) doesn't guarantee keys come back in the order you added them — if order matters, loop over a sorted array of the keys instead (structKeyArray(myStruct) then sort it).
Trying to modify the array you're looping over
Adding or removing elements from an array while a for...in loop is iterating over it can produce unpredictable results. Build a new array instead, or loop by index with a plain for loop if you need to modify in place.
Forgetting query.currentRow starts at 1
Like arrays, query row numbers are 1-indexed, not 0-indexed — the first row is currentRow 1.
Best Practices
- Use for...in whenever you just need each item and don't care about its position.
- Fall back to a manual for loop when you specifically need the index — to compare neighboring items, for example.
- Name the loop variable after what it actually is (fruit, row, key) rather than a generic item.
Interview Questions
What does for...in give you when looping over an array?
Each element's value directly, without a manual index counter.
What does for...in give you when looping over a struct?
Each key — use user[key] to get the matching value.
What's the tag-syntax equivalent of for...in for an array?
<cfloop array="#arr#" index="item">.
What's the tag-syntax equivalent of for...in for a struct?
<cfloop collection="#myStruct#" item="key">.
Summary
In this lesson, you learned CFScript's for...in loop for arrays, structs, lists, and query rows, along with the matching <cfloop> tag-syntax variant for each. Reach for this style whenever you just need each item, and save manual index-based loops for when you specifically need the position.
What's Next?
The next lesson is a complete reference for <cfloop> — every loop type it supports in tag syntax, including options this module hasn't covered yet like file loops and query grouping.