Ask a Jedi: Round robin display of data

This post is more than 2 years old.

This came in via my forums today and I thought it was too interesting to not blog about. If you read his post, you can see he needs to support rotating data such that A B C changes to B A C. However, he also has groupings within his data. So imagine now you have ABC in the first group and DE in the second. Your rotation now needs to be "in" group, so on your first iteration you have: A B C D E. On your second you rotate each group to end with: B C A E D. I whipped up the following solution and I'd love to see alternatives.

I began by creating a data structure. SuperAlly mentioned a query but for now I assume the query data is converted into a 2D array.

<cfset data = [ ["a", "b", "c"], ["d", "e"] ]>

Next I whipped up a quick UDF that would display this data. It assumes you pass in a 2D array and simply loops through each element:

<cfscript> function render(arr) { for(var i=1; i<=arrayLen(arr); i++) { for(var x=1; x<=arrayLen(arr[i]); x++) { writeOutput(arr[i][x] & " "); } } } </cfscript>

I displayed this to ensure everything was kosher:

<cfoutput> #render(data)# <p/> </cfoutput>

This returned "a b c d e" as I expected. Ok, now for the fun part. In theory, all I have to do is remove the item from the beginning of each array and drop it on top.

<cfloop index="i" from="1" to="#arrayLen(data)#"> <cfset movingItem = data[i][1]> <cfset arrayDeleteAt(data[i],1)> <cfset arrayAppend(data[i], movingItem)> </cfloop>

As you can see, I loop over data, which is the core data holder. Each element of data is an array itself. I grab the first item, delete it, and then simply add it to the end of the array. Here is a complete script with a few sample runs:

<cfset data = [ ["a", "b", "c"], ["d", "e"] ]>

<cfscript> function render(arr) { for(var i=1; i<=arrayLen(arr); i++) { for(var x=1; x<=arrayLen(arr[i]); x++) { writeOutput(arr[i][x] & " "); } } } </cfscript>

<cfoutput> #render(data)# <p/> </cfoutput>

<cfloop index="i" from="1" to="#arrayLen(data)#"> <cfset movingItem = data[i][1]> <cfset arrayDeleteAt(data[i],1)> <cfset arrayAppend(data[i], movingItem)> </cfloop>

<cfoutput> #render(data)# <p/> </cfoutput>

<cfloop index="i" from="1" to="#arrayLen(data)#"> <cfset movingItem = data[i][1]> <cfset arrayDeleteAt(data[i],1)> <cfset arrayAppend(data[i], movingItem)> </cfloop>

<cfoutput> #render(data)# <p/> </cfoutput>

And the output:

a b c d e

b c a e d

c a b d e

Notice how the second group, D E, correctly works even though it is smaller than the first group, A B C.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Chuck Savage posted on 11/18/2009 at 12:16 AM

Wouldn't it be easier to do an array of linked lists? Then you'd only need to pop the front and put it to the end for each one.

Comment 2 by Raymond Camden posted on 11/18/2009 at 12:17 AM

I was trying to stay within all "native" CFML data structures.

Comment 3 by Chuck Savage posted on 11/18/2009 at 1:09 AM

Yeah, well its not as easy as I first thought. Almost done writing a CFC to handle the linked list, and there is a lot of boiler plate code to Add(), GetNextValue() to iterate through, etc.

Comment 4 by SuperAlly posted on 11/18/2009 at 3:34 AM

Thanks very much for the fast response, I greatly appreciate it.
I will let you know how I do as soon as I have a chance to sit down with it.

Thanks again.

Comment 5 by Chuck Savage posted on 11/18/2009 at 4:07 AM

Well, I finished creating two CFC's. A good LinkedList.cfc, and the LinkedSeries.cfc that is a wrapper for 10 linked lists.

Here they are in action,

The linked series: http://searisen.com/mg/?eve...

The linked list: http://searisen.com/mg/?eve...

If anyone has suggestions to licenses for releasing the CFC's, can you point me in the right direction?

Comment 6 by Adam Presley posted on 11/18/2009 at 4:25 AM

Interesting post Ray. You inspired me to nerd out and see if there is a Java way. Check out the results at http://blog.adampresley.com... . Keep up the interesting posts, and happy coding!

Comment 7 by Chuck Savage posted on 11/18/2009 at 5:46 AM

Adam thanks for that post, shows me something new about ColdFusion. Where do I find more about the various Java.utils that ColdFusion includes?

Comment 8 by Adam Presley posted on 11/18/2009 at 7:11 AM

You can see the actual Utility classes at http://java.sun.com/javase/.... However it helps to know what the foundation of various CF data types are. For example, an array is java.util.Vector and a structure is java.util.Hashtable. Basic variables are based on java.lang.String, and are dynamically type-cast to double, int, etc... From there you can see what is available under the hood, and what other utility classes can affect these.

Happy coding!

Comment 9 by SuperAlly posted on 3/12/2010 at 9:34 PM

Again, Ray, thank you very much for taking the time to go through this, it is much appreciated. Unfortunately the project got bumped for the last few months and I am only now being asked to go back to it...
Unfortunately though, it seems (at least to me :) ) to be a lot more complicated than I had originally thought. There could, in theory, be far more than two groups of results within the query that would need to be sorted amongst themselves and then, being that it's a query of a db to begin with, every time someone viewed the results, it would start from the same place, therefore always removing the same first items from each 'group' to place at the end. (That is, even if I COULD get my query converted to an array properly without errors!! )
I think I may need to rethink my entire approach. If I look at the page and see a,b,c,d,e then you look at the page, you should see b,c,a,e,d, then I refresh the page, I should see c,a,b,d,e. So, I guess I need to keep track of which record was last viewed as the first record.
Anyway, sorry to ramble, apologies for the delayed response, and thanks again for the help!

Comment 10 by Raymond Camden posted on 3/12/2010 at 9:40 PM

No worries - hope you get it solved! As just an FYI, you can use an Application variable to store the last view. That ensures I get view 1, then you get view 2, and if you reload you get view 3, then if I reload I get view 4. This is a fairly typical 'banner rotation' type script.