views:

362

answers:

4

I have an array of structs in ColdFusion. I'd like to sort this array based on one of the attributes in the structs. How can I achieve this? I've found the StructSort function, but it takes a structure and I have an array.

If this is not possible purely in ColdFusion, is it possible in Java somehow (maybe using Arrays.sort(Object[], Comparator))?

+5  A: 

As usual, CFLib.org has exactly what you want.

http://cflib.org/udf/ArrayOfStructsSort

<cfscript>
/**
* Sorts an array of structures based on a key in the structures.
*
* @param aofS      Array of structures.
* @param key      Key to sort by.
* @param sortOrder      Order to sort by, asc or desc.
* @param sortType      Text, textnocase, or numeric.
* @param delim      Delimiter used for temporary data storage. Must not exist in data. Defaults to a period.
* @return Returns a sorted array.
* @author Nathan Dintenfass ([email protected])
* @version 1, December 10, 2001
*/
function arrayOfStructsSort(aOfS,key){
        //by default we'll use an ascending sort
        var sortOrder = "asc";        
        //by default, we'll use a textnocase sort
        var sortType = "textnocase";
        //by default, use ascii character 30 as the delim
        var delim = ".";
        //make an array to hold the sort stuff
        var sortArray = arraynew(1);
        //make an array to return
        var returnArray = arraynew(1);
        //grab the number of elements in the array (used in the loops)
        var count = arrayLen(aOfS);
        //make a variable to use in the loop
        var ii = 1;
        //if there is a 3rd argument, set the sortOrder
        if(arraylen(arguments) GT 2)
            sortOrder = arguments[3];
        //if there is a 4th argument, set the sortType
        if(arraylen(arguments) GT 3)
            sortType = arguments[4];
        //if there is a 5th argument, set the delim
        if(arraylen(arguments) GT 4)
            delim = arguments[5];
        //loop over the array of structs, building the sortArray
        for(ii = 1; ii lte count; ii = ii + 1)
            sortArray[ii] = aOfS[ii][key] & delim & ii;
        //now sort the array
        arraySort(sortArray,sortType,sortOrder);
        //now build the return array
        for(ii = 1; ii lte count; ii = ii + 1)
            returnArray[ii] = aOfS[listLast(sortArray[ii],delim)];
        //return the array
        return returnArray;
}
</cfscript>
Al Everett
+1  A: 

Here is something that closely resembles the original StructSort(). It also supports the pathToSubElement argument.

<cffunction name="ArrayOfStructSort" returntype="array" access="public">
  <cfargument name="base" type="array" required="yes" />
  <cfargument name="sortType" type="string" required="no" default="text" />
  <cfargument name="sortOrder" type="string" required="no" default="ASC" />
  <cfargument name="pathToSubElement" type="string" required="no" default="" />

  <cfset var tmpStruct = StructNew()>
  <cfset var returnVal = ArrayNew(1)>
  <cfset var i = 0>
  <cfset var keys = "">

  <cfloop from="1" to="#ArrayLen(base)#" index="i">
    <cfset tmpStruct[i] = base[i]>
  </cfloop>

  <cfset keys = StructSort(tmpStruct, sortType, sortOrder, pathToSubElement)>

  <cfloop from="1" to="#ArrayLen(keys)#" index="i">
    <cfset returnVal[i] = tmpStruct[keys[i]]>
  </cfloop>

  <cfreturn returnVal>
</cffunction>
Tomalak
"keys" needs to be var-scoped, I believe.
Edward M Smith
@Edward: Absolutely, I've missed that one. Thanks for the hint.
Tomalak
+1  A: 

In case you don't want to use custom methods, Coldfusion has structSort method http://www.cfquickdocs.com/cf8/#StructSort . Yes it sorts structure with nested structures, BUT returns array so could be used to achieve same result.

zarko.susnjar
+2  A: 

The accepted solution (from CFLib.org) is NOT safe. I experimented with this for something I needed to do at work and found that it returns incorrect results when sorting numeric with floats.

For example if I have these structs: (pseudocode)


a = ArrayNew(1);

s = StructNew();
s.name = 'orange';
s.weight = 200;
ArrayAppend(a, s);

s = StructNew();
s.name = 'strawberry';
s.weight = 28;
ArrayAppend(a, s);

s = StructNew();
s.name = 'banana';
s.weight = 90.55;
ArrayAppend(a, s);

sorted_array = arrayOfStructsSort(a, 'weight', 'asc', 'numeric');

Iterate over the sorted array and print the name & weight. It won't be in the right order, and this is a limitation of mixing an arbitrary key with the value being sorted.

orange80