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

Posted September 20, 2006 at 1:48 PM by Ben Nadel

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

Sep 29, 2006 at 8:19 AM // reply »
20 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!


Sep 29, 2006 at 9:38 AM // reply »
11,246 Comments

What can I say, Mark Mandel is the man :)


Jan 28, 2007 at 4:17 AM // reply »
1 Comments

THANKS for this... I've recently been dabbling in what I can do with utilizing the base java libraries and this is just slick!


Jul 9, 2008 at 7:30 PM // reply »
3 Comments

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)!


Jul 14, 2008 at 8:51 AM // reply »
11,246 Comments

@Kay,

Anytime :) Thanks for dropping by.


Nov 24, 2008 at 9:20 PM // reply »
1 Comments

Many thanks! Love your site!


Oct 6, 2010 at 8:57 PM // reply »
14 Comments

wowwwwwwwww! This cannot be easier!

This can be used to shuffle a list as well.

Put them into my framework ;-)

Thanks Ben and Mark.


Oct 10, 2010 at 4:07 PM // reply »
11,246 Comments

@Khoa,

Yeah, that Collections is pretty awesome. When Mark showed this to me, it totally blew my mind.


Dec 7, 2010 at 6:58 AM // reply »
3 Comments

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.


Dec 15, 2010 at 5:56 PM // reply »
10 Comments

@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;


Dec 16, 2010 at 2:02 AM // reply »
3 Comments

@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]"


Dec 16, 2010 at 9:54 AM // reply »
10 Comments

@Alexey,

I ran the test with 4 items, 3 items, and 2 items in the array.

Curt


Dec 16, 2010 at 10:08 AM // reply »
3 Comments

I have a lot of duplicates using your code. Railo 3.2.005 (jdk 1.6.0_22). Bug maybe?


Dec 16, 2010 at 10:10 AM // reply »
10 Comments

Hmm, I ran mine against ACF 901. Maybe that is the difference. I will run it against my Railo and see what happens.

Thanks,
Curt


Dec 16, 2010 at 10:26 AM // reply »
10 Comments

Sure enough. It fails under Railo. Lots of duplicates.

Interesting. I wonder why? Java versions?

Good to know.

Thanks,
Curt


Jan 7, 2011 at 9:23 PM // reply »
11,246 Comments

@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?


Apr 6, 2011 at 8:34 AM // reply »
211 Comments

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.


Apr 6, 2011 at 9:05 AM // reply »
10 Comments

@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


Apr 6, 2011 at 10:41 AM // reply »
211 Comments

@Curt: Apparently this was a bug that was already fixed:
https://issues.jboss.org/browse/RAILO-1291


Apr 6, 2011 at 11:06 AM // reply »
1 Comments

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.


Apr 6, 2011 at 11:36 AM // reply »
10 Comments

@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


Apr 6, 2011 at 11:37 AM // reply »
211 Comments

@Curt: Slacker. ;)


Apr 7, 2011 at 7:38 AM // reply »
4 Comments

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>


Apr 7, 2011 at 10:34 PM // reply »
11,246 Comments

@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.


Apr 7, 2011 at 10:46 PM // reply »
4 Comments

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


Sep 1, 2011 at 12:19 PM // reply »
4 Comments

This is awesome. Saved my bacon today. Or shuffled it anyway.


Feb 29, 2012 at 8:38 AM // reply »
6 Comments

Thanks! Worked like a charm.

Everyday I'm java.util.Collections.Shuffle()'ng


Mar 27, 2012 at 10:44 PM // reply »
2 Comments

This is just the bees knees. I was trying to figure out how to randomise an orm entity array. This worked perfectly.

--Dave



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 23, 2013 at 9:52 PM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Muhmmadibn Did you figure out a solution to launching PDFs? I am running into the same issues myself. There is no way to close the PDF or go back once you launch it. Thanks in advance! ... read »
May 23, 2013 at 6:06 PM
The Girl Who Broke My Heart, And Made Me A Better Person
Good day,ladies and gentle men, my name is Dr AMADI the great spell caster in Africa, i have help so many people for different kind of problems,who say there is no solution to problems on earth, that ... read »
May 23, 2013 at 4:26 PM
ColdFusion QueryAppend( qOne, qTwo )
@Heather, Glad people are still getting value out of this! ... read »
May 23, 2013 at 3:49 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, I meant the code at the bottom (not the video). I did try to experiment with an intermediary variable, like: value = users.id[ i ]; arrayContains( userIDs, value ); ... but t ... read »
May 23, 2013 at 11:06 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Are you talking about As Number: YES As String: YES As Java: YES? If so, that's with 3 different ways of referencing the constant 1, not users.id[1]. Query object references(*) are what seem ... read »
May 23, 2013 at 9:55 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Dan, According to the CF Admin, I'm running Java "1.6.0_45". As far as the DB column, in the database it's an INT. I'll see if I can dig into what CF sees it as. @WebManWalking, But h ... read »
May 23, 2013 at 9:49 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, I think the problem is that we're used to loose typing in ColdFusion, like JavaScript. If a value is a number but it's needed in an expression to be a string, noooo problem. I've encountered ... read »
May 23, 2013 at 9:47 AM
ColdFusion QueryAppend( qOne, qTwo )
You rock! Thank you, thank you, thank you!!! ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools