Definition
cfloop is a ColdFusion tag used to repeat a block of code multiple times. In simple terms, it lets you iterate over items (like arrays, lists, files, queries, or structures), or run a loop based on a numeric range or a logical condition. It’s one of the core control-flow Features in CFML (ColdFusion Markup Language), available in both tag-based Syntax and script-based (cfscript) equivalents.
How It Works
At its core, cfloop provides several modes of Iteration. You select the mode by using specific attributes on the tag. Each mode targets a common programming need: fixed ranges, conditions, lists, arrays, structures, queries, files, and repeated counts.
Loop Types and Syntax
- Numeric range loop (index, from, to, step)
- Conditional loop (condition)
- Times loop (times)
- List loop (list, delimiters)
- Array loop (array)
- Structure/collection loop (collection)
- Query loop (query, startrow, endrow, group)
- File loop (file)
Each type has an attribute set that you combine with an index/item variable.
Numeric Range Loop
- Use when you know the start, end, and increment.
- Syntax (tag):
- Equivalent script:
for (i = 1; i <= 10; i++) {
// Your code with i
}
Conditional Loop (While-style)
- Evaluates a boolean expression every Iteration.
- Syntax (tag):
- Equivalent script:
attempts = 0;
while (attempts < 3) {
// do something
attempts++;
}
Tip: Use
Times Loop
- Run code a fixed number of times without specifying from/to.
- Syntax (tag):
List Loop
- Iterate over items in a delimited string (CSV, pipe, etc.).
- Syntax (tag):
Array Loop
- Iterate elements of a ColdFusion array.
- Syntax (tag):
- Equivalent script:
for (item in myArray) {
// use item
}
Structure/Collection Loop
- Iterate keys in a struct (map/dictionary).
- Syntax (tag):
- Equivalent script:
for (key in myStruct) {
// myStruct[key]
}
Query Loop
- Loop over rows in a query object.
- Syntax (tag):
You can also group rows by a column using the group attribute to process grouped data.
- Equivalent script:
for (rowIndex = 1; rowIndex <= q.recordCount; rowIndex++) {
// q.columnName[rowIndex]
}
File Loop
- Read a text file one line at a time.
- Syntax (tag):
This streams the file efficiently without loading it all in memory.
Use Cases
Processing Query Results
- Render dynamic HTML tables or export data.
- Aggregate totals, group by a field, or paginate.
Transforming Lists and Arrays
- Clean and sanitize CSV input.
- Normalize user-submitted lists into arrays for validation.
Reading Large Files Incrementally
- Parse logs, import CSVs, or process streaming text line-by-line with low memory usage.
Repetitive Tasks and Counters
- Retry logic with a maximum number of attempts.
- Polling loops with a condition and a timeout guard.
Working with Structures
- Build JSON from a struct or display Configuration keys and values.
Practical Example: Building a Sales Report From a Query
The following example fetches sales rows, groups by region, calculates totals per region, and outputs a report. It demonstrates a query loop, grouping, and a structure loop for subtotals.
SELECT region, salesperson, amount
FROM sales
WHERE sale_date BETWEEN
AND
ORDER BY region, salesperson
Sales Report
#r# — Total: #numberFormat(totalsByRegion[r], ‘9,999.99’)#
#qSales.salesperson#: #numberFormat(qSales.amount, ‘9,999.99’)#
Notes:
- We perform one pass to compute totals, then loop again to print grouped details.
- The group attribute helps process rows per region.
- Using cfqueryparam prevents SQL injection and improves Performance.
Best practices
- Prefer the most specific loop type: use array/list/collection/query modes when applicable instead of generic index loops that fetch by position.
- Manage loop termination clearly. When looping by condition, provide a safe exit and consider adding a counter and a maximum threshold.
- Use cfbreak and cfcontinue judiciously to Control flow within loops.
- Keep Business logic and presentation separate. Do computations in one pass, format output in another, or use cfscript to prepare data before rendering.
- For query loops, avoid expensive operations inside each iteration (like nested queries). Fetch all needed data first or use joins and aggregates at the SQL level.
- Validate and sanitize list and file inputs. Control delimiters explicitly and handle unexpected characters or encodings.
- For file loops, prefer absolute or controlled paths. Consider file locking when writing, and set request timeouts for long tasks.
- In functions, scope your loop variables (var/local) to prevent leaking into other scopes.
- When outputting from loops, avoid excessive string concatenation. Use array buffers or StringBuilder-like patterns (arrayAppend + arrayToList) for large outputs.
- For long-running loops, consider request timeouts (cfsetting requestTimeout) and optionally provide progress feedback.
Key Points and Quick reference
- CFLOOP modes: numeric range, condition, times, list, array, collection/struct, query, file.
- Script equivalents exist for every mode via for, while, for-in constructs.
- Control flow helpers: cfbreak, cfcontinue.
- Query loops support startrow, endrow, and optional group processing.
- Use delimiters for list loops, and index/item names to refer to the current value.
Quick Comparison:
| Loop type | Best for | Key attributes | Notes |
|---|---|---|---|
| Numeric range | Fixed counts with index | index, from, to, step | Classic for-loop behavior |
| Condition | While-style control | condition | Ensure termination condition |
| Times | Simple repetition | times, index | No need to specify from/to |
| List | Delimited strings | list, index, delimiters | Good for CSV-like input |
| Array | CF arrays | array, index | Clean and readable |
| Collection | Structs (maps) | collection, item | Iterates keys |
| Query | Query rows | query, startrow, endrow, group | Row-wise processing |
| File | Text files | file, index | Line-by-line reading |
Common pitfalls and Debugging
- Off-by-one errors: Verify inclusive ranges (to is inclusive). Double-check your from/to/step and condition logic.
- Infinite loops: Always enforce exit conditions; add a secondary counter or timeout guard.
- Query row access in script: Be consistent using q.column[row] access; mixing row-major vs column-major access causes confusion.
- Nulls and empty strings: Use isNull and len trims appropriately inside loops to prevent errors.
- Encoding issues in file loops: Specify or detect the correct charset when reading/writing files to avoid garbled text.
- Scope collisions: Avoid using generic variable names like i or item in large functions without scoping them local/var.
Performance Tips
- Minimize work inside hot loops: precompute constants, cache lookups, and extract heavy operations outside the loop.
- Use native loop modes (array/list/query) to leverage optimized iteration instead of manual indexing.
- For large string output, build in-memory buffers and write once rather than concatenating repeatedly inside the loop.
- Grouped query processing: If you need aggregates, push summations to SQL; let the database do the heavy lifting.
- For massive file processing, stream and batch commits; avoid loading entire files into memory.
Related Syntax Examples
- Break and continue:
- Looping over characters in a string:
- Paginated query loop:
FAQ
What is the difference between CFLOOP and cfoutput for query iteration?
cfoutput can iterate a query and output values directly, but CFLOOP is more flexible for logic-heavy tasks, grouping, and precise control. Use CFLOOP for Data processing and CFOUTPUT for presentation, or combine them thoughtfully.
Can I break out of a CFLOOP early?
Yes. Use
How do I avoid infinite loops with the condition attribute?
Always change state inside the loop so the condition eventually becomes false. Add a safety counter and, if necessary, a timestamp check to break after a maximum duration or iterations.
Is there a performance difference between array and list loops?
Yes. Iterating a native array is generally faster and safer than parsing a delimited list. Convert lists to arrays (listToArray) for heavy processing.
Can CFLOOP read large files safely?
Yes. The file mode reads line-by-line, which is memory efficient. Still, validate file size, handle encoding, and set request timeouts for very large inputs.
