Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at Scotch On The Rock (SOTR) 2010 (London) with:

Randomly Sort A ColdFusion Array (Updated) - Thanks Mark Mandel

By Ben Nadel on
Tags: ColdFusion

I had recently posted a ColdFusion solution to randomly sorting an array. Mark Mandel graciously pointed out that in Java, there is already a way to do this utilizing the java.util.Collections class. The java.util.Collections class is comprised solely of static methods that manipulate collections. It just so happens that it has a method: Shuffle().

Implementing it in ColdFusion could not be easier:

  • <!--- Create an array. --->
  • <cfset arrGirls = ListToArray(
  • "Sarah,Ashley,Anna,Libby"
  • ) />
  •  
  • <!--- Shuffle it (ie. randomly sort it). --->
  • <cfset CreateObject(
  • "java",
  • "java.util.Collections"
  • ).Shuffle(
  • arrGirls
  • ) />

The code is compact so it may be hard to read. First, we are creating an instance of the Collections class:

  • <cfset CreateObject( "java", "java.util.Collections" ) />

This class has NO constructors, which is why we are not calling any Init() methods. Once we have the instance, we then call the static method Shuffle() and pass in the array, arrGirls. This method does not return a value. It is updating the array by reference, not value.

This should accept anything that implements the list interface, java.util.List.

How easy is that? Thanks a bunch Mark Mandel! Man, I really need to learn more about the Java 2 class libraries. There is so much stuff in there that is SOOO awesome for ColdFusion.




Reader Comments

Wow, that's so much simpler than my method of getting a random selection of sidebar buttons from a SQL query (which basically entailed getting the first random number, getting the second and checking it wasn't the same as the first, getting the third etc...).

Not what you'd call efficient code, but I couldn't see an alternative!

Heh, awesome. I spend five minutes refamiliarising myself with CF's built in randomizing functions in preparation for thinking through a solution to randomizing an array. Then on a whim I google it and BAM, first result, a solution in one line of code. I love CF - thanks Ben (and Mark)!

wowwwwwwwww! This cannot be easier!

This can be used to shuffle a list as well.

Put them into my framework ;-)

Thanks Ben and Mark.

Hmm.. I found that using an array of 5 elements or less causes repeats. E.g. aMy = [1,2,3,4,5]; shuffle(aMy); may return [1,1,4,4,5] or [1,2,2,3,5]...
If the array greater than 5 elements repeats doesn't occurs, just random sorting.

@Ben,

Great post, thanks. And thanks Mark :)

@Alexey,

Why do you think this is the case. I tested with the following code and over 1000000 tries I never once got a duplicate.

  • for (var t=0; t lte 1000000; t=t+1) {
  • javaCollections = CreateObject( "java", "java.util.Collections");
  • lhs = createObject("java", "java.util.LinkedHashSet");
  • aMy = [1,2,3,4,5];
  • javaCollections.shuffle(aMy);
  • //remove duplicates
  • lhs.init( aMy );
  • aMy.clear();
  • aMy.addAll( lhs );
  • //still 5, if no dump
  • if (arrayLen(aMy) NEQ 5)
  • writeDump(aMy);
  • }
  • abort;

@Curt
Thanks for your time! My fault, I accidentally misled you.
Correction: "I found that using an array of 4 elements or less causes
repeats. E.g. aMy = [1,2,3,4]; shuffle(aMy); may return [1,1,4,4] or [1,2,2,3]"

@Alexey, @Curt,

Hmm, that is very curious. I haven't use Railo myself; but, since we are invoking a Java Collections explicitly (and not some CF class), you'd think that the behavior would be exactly the same. If anyone gets to the bottom of this, I'd be interested in hearing what it is.

Since I will assume the Java classes are the same (the Core java classes), I will assume that it has something to do with the Java class uses to implement the Array. Maybe there's a threading issue or something?

Just an FYI: This was brought up on the Railo group recently. As soon as we get an answer from Micha, I'll post something here to clear it up. Someone suggested that it may be because Railo passes by reference, not by value.

@Todd,

Sweet, thanks for the update Todd. Its funny because I hadn't really thought about this at all until yesterday and was just looking back into it.

Curt

First, we have solved the problem
https://issues.jboss.org/browse/RAILO-1291

Problem was the implementation of the method java.util.List#set(int, java.lang.Object), this method is never used by Railo, but supported because we have implemented the List interface for ArrayImpl (for compatibility reason) and of course the "shuffle" method use this method. the return value of the "set" method should be the element previously at the specified position, but railo has returned the object set to the array, like every set action in Railo does and because of that the failure was happening.

/micha

BTW: this has nothing to do with "pass-by-value", java does not know the "pass-by-value" concept for objects, this is just happening in ACF and to do so, ACF clones array before passing/returning to/from a UDF or assign to a variable, in this case ACF does not clone the array as well.

@Michael and @Todd,

Sweet, Thanks for the update. I hadn't used it on Railo since I discovered this. Glad to know I can now. (I probably should have filed the bug for this, my bad).

Anyway, great work.

Curt

Kudos to the Railo guys for addressing this one so quickly.

If you want to use the Shuffle method in railo and like me either can't wait for the new release or are incapable of compiling the patch :-), the following work-around seems to work.

Basically it avoids using the railo Collections implementation by converting the CF Array to native Java objects, does the shuffle on those and then converts back to a CF Array again. It should work in both Adobe CF and Railo.

There may be a more efficient way to do the final conversion back to a CF array, but I'm only ever working with a list of 3 elements to be shuffled in my app :-)

<cffunction name="arrayShuffle" returntype="array">
<cfargument name="arrayIn" type="array">

<!--- NB: Only designed to work with single dimensioned arrays
--->

<cfset var jArrayList = CreateObject("java",
'java.util.ArrayList').init(arguments.arrayIn)>
<cfset var cfArray = ArrayNew(1)>
<cfset var objIterator = "">

<!--- Shuffle jArrayList --->
<cfset CreateObject(
"java",
"java.util.Collections"
).Shuffle(
jArrayList
) />

<!--- Now convert it back to a ColdFusion Array --->
<cfset objIterator = jArrayList.iterator()>
<cfloop condition="objIterator.hasNext()">
<cfset ArrayAppend(cfArray, objIterator.next())>
</cfloop>

<cfreturn cfArray>

</cffunction>

@Michael,

Awesome stuff - you guys are so fast on the bug fixes. I am consistently impressed with your team!

@Andrew,

Cool stuff; but, the real question is - can you add the arrayShuffle() to the list of globally available functions of the language? I keep hear about how the Railo codebase can be programmatically extended. This seems like one of the coolest features.

Hey Ben - I actually wasn't aware of this, but it appears that you certainly can.

I just found a blog post describing how it's done if you don't mind me posting the link:

http://www.railo.ch/blog/index.cfm/2009/7/23/Railo-31-Building-your-own-BuiltInFunction