1

Every so often, either in display code or in assembling a string, I'll be making a list and need to figure out how to insert commas in that list.

This is how I usually do it:

<cfset hide_comma=true>
<cfloop ... some kind of loop ...>
    <cfif hide_comma><cfset hide_comma=false><cfelse>,</cfif>
    .... rest of code here ...
</cfloop>

I'm wondering if there's a cleaner way of doing this. I realize one option would be something like the following:

<cfset output_as_array = []>
<cfloop ... some kind of loop ...>
    <cfset loop_output = "">
    ... rest of code here, but append all output to loop output instead ...
    <cfset ArrayAppend(output_as_array, trim(loop_output))>
</cfloop>
<cfoutput>#ArrayToList(output_as_array, ", ")#</cfoutput>

But that doesn't really seem any clearer.

In Django, in contrast, each loop has a built in counter so I can write something like:

{% for ... some kind of loop ... %}
    {% if not forloop.first %},{% endif %}
    ... rest of code here ...
{% endfor %}

Pretty much the same logic, only there's already a built-in way to check loop state, rather than having to create one on my own. I know that when looping through a <cfoutput query=...> I can use QueryName.RowCount for this purpose, but can't find anything similar in the documentation for CFLOOPs.

Jordan Reiter
  • 20,467
  • 11
  • 95
  • 161

3 Answers3

6

For compiling a variable, using the ValueList (for queries) and ArrayToList functions is a sensible approach.

If not dealing with a query or array, build an array using ArrayAppend then convert to string with ArrayToList.

(Note: ListAppend is ok for one or two items, but in a long loop it's slower than using ArrayAppend+ArrayToList - see info here.)


Regarding:

Pretty much the same logic, only there's already a built-in way to check loop state, rather than having to create one on my own. I know that when looping through a I can use QueryName.RowCount for this purpose, but can't find anything similar in the documentation for CFLOOPs.

Short answer: no, there isn't a built-in automatic index for loops in ColdFusion, other than standard from/to and query loops.


For index loops, you can of course use the index:

<cfloop index="Index" from=1 to=#ArrayLen(MyArray)#>
    <cfif Index GT 1> Not First Row </cfif>
</cfloop>

For query loops, use rowcount:

<cfloop query="MyQuery">
    <cfif MyQuery.RowCount GT 1> Not First Row </cfif>
</cfloop>

For looping through items, you need to create your own variable:

<cfset Row = 0 />
<cfloop index="Item" array=#MyArray#>
    <cfif ++Row GT 1> Not First Row </cfif>
</cfloop>

<cfset Row= 0 />
<cfloop item="Item" collection=#MyStruct# >
    <cfif ++Row GT 1> Not First Row </cfif>
</cfloop>

In Railo, you can specify both index and item attributes and have both values available:

<cfloop index="Index" item="Item" array=#MyArray#>
    <cfif Index GT 1> Not First Row </cfif>
</cfloop>

The index refers to the key though, so you cannot do that with structs (you'll get the key name, not row number).

Community
  • 1
  • 1
Peter Boughton
  • 110,170
  • 32
  • 120
  • 176
3

You can use ListAppend()

Sample code:

<cfset myList = "" />
<cfloop ....>
    <cfset myList = listAppend( myList, 'some value') />
</cfloop>

This will give you a comma delimited list. If you want/need to list delimited by another character, you can specify that as a second argument in listAppend()

I just ran a test here - and it seems that using arrayAppend() then arrayToList() is much faster than using listAppend(). So the code using array append should be the best path to take.

Scott Stroz
  • 7,510
  • 2
  • 21
  • 25
  • Fair enough. My understanding is that for large amounts of content, ListAppend becomes less efficient. My main query is whether there's some other mechanism for tracking whether or not I can output a comma, or if I have to assemble either a list or an array and then output it. – Jordan Reiter Aug 23 '13 at 16:08
  • you could use `arrayAppend` and then `arrayToList(yourArray)` – Matt Busche Aug 23 '13 at 16:19
  • Well, if your list is very long, maybe there is an even better solution. I have used listAppend() and arrayAppend() with hundreds of items without issue. If you are making a list from a column in a query object, you can also use valueList() - http://www.cfquickdocs.com/#ValueList – Scott Stroz Aug 23 '13 at 16:19
  • @MattBusche that was the route I took in my question. – Jordan Reiter Aug 23 '13 at 16:20
  • @ScottStroz these are generally larger, more unwieldy portions of text which is why really it'd be more ideal to treat this as a template rather than assembling a variable. Also usually the text that I'm appending has commas in it already. – Jordan Reiter Aug 23 '13 at 16:23
  • Seems like an odd use case...but, I just did some tests and it seems that `arrayAppend()`/`arrayToList()` path is the way to go. Here is a gist of a test I just set up and `arrayAppend()` kicks the crap out of `listAppend()` - https://gist.github.com/boyzoid/8b14bffb5360265a7829 – Scott Stroz Aug 23 '13 at 16:31
  • @JordanReiter It should not matter if what you are appending to the list has commas, unless you need to iterate over that list after it is created. Either way, based on my completely unofficial test case, `arrayAppend()` is much more performant. – Scott Stroz Aug 23 '13 at 16:35
  • @ScottStroz the reason it would be an odd use case is the crux of my question -- when outputting large quantities of text that need to be separated by commas (creating a complex array in JavaScript is a good example), looks like the only options are using ArrayAppend/ListAppend or my solution at the top. – Jordan Reiter Aug 23 '13 at 16:58
  • _"...creating a complex array in JavaScript..."_ - create it as a CFML array then use [serializeJson](https://learn.adobe.com/wiki/display/coldfusionen/serializeJson) ? – Peter Boughton Aug 23 '13 at 17:01
  • @PeterBoughton there is a bit of logic on each loop, plus the format is different (it's not a straight translation from the CF to the JavaScript) so writing the code to first convert it into a different array of structs and then serializing doesn't seem like an improvement. – Jordan Reiter Aug 23 '13 at 17:06
0

I like to do something like this:

<cfset separator="">
<cfloop ...>
  #variables.separator#
  <!--- rest of code here --->
  <cfset separator=", ">
</cfloop>
ale
  • 6,369
  • 7
  • 55
  • 65