如何在ColdFusion中对结构数组进行排序

时间:2010-04-16 14:45:13

标签: sorting coldfusion struct

我在ColdFusion中有一系列结构。我想根据结构中的一个属性对这个数组进行排序。我怎样才能做到这一点?我找到了StructSort函数,但它需要一个结构,我有一个数组。

如果纯粹在ColdFusion中这是不可能的,那么在Java中是否可能以某种方式(可能使用Arrays.sort(Object[], Comparator))?

10 个答案:

答案 0 :(得分:14)

这是与原始StructSort()非常相似的东西。它还支持pathToSubElement参数。

<cffunction name="ArrayOfStructSort" returntype="array" access="public" output="no">
  <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>

用法/测试:

<cfscript> 
  arr = ArrayNew(1);

  for (i = 1; i lte 5; i = i + 1) {
    s = StructNew();
    s.a.b = 6 - i;
    ArrayAppend(arr, s);
  }
</cfscript> 

<cfset sorted = ArrayOfStructSort(arr, "numeric", "asc", "a.b")>

<table><tr>
  <td><cfdump var="#arr#"></td>
  <td><cfdump var="#sorted#"></td>
</tr></table>

结果:

ArrayOfStructSort Result

答案 1 :(得分:12)

像往常一样,CFLib.org正是你想要的。

http://cflib.org/udf/ArrayOfStructsSort

/**
* 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 (nathan@changemedia.com)
* @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;
}

答案 2 :(得分:5)

公认的解决方案(来自CFLib.org)并不安全。我尝试了这个我需要在工作中做的事情,并发现在使用浮点数对数字进行排序时会返回错误的结果。

例如,如果我有这些结构:(伪代码)


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');

迭代已排序的数组并打印名称&amp;重量。 它的顺序不正确,这是混合的限制 一个任意键,值被排序。

答案 3 :(得分:4)

您可以使用Underscore.cfc library来完成您想要的任务:

arrayOfStructs = [
    {myAttribute: 10},
    {myAttribute: 30},
    {myAttribute: 20}
];

_ = new Underscore();

sortedArray = _.sortBy(arrayOfStructs, function (struct) {
    return struct.myAttribute;
});

Underscore.cfc允许您定义自定义比较器并委托给arraySort()。您可以使用它来排序数组,结构,查询或字符串列表,但它总是返回一个数组。

(免责声明:我写过Underscore.cfc)

答案 4 :(得分:2)

我想把我的两分钱扔到这里。我遇到了一个需要使用多个键对结构数组进行排序的情况。我使用构造的查询来完成排序。该函数将结构数组作为第一个参数,然后是一个表示排序顺序的结构数组,如下所示:

<cfset result = sortArrayOfStructsUsingQuery(myArrayOfStructs,[
{name = "price", type = "decimal", sortOrder = "asc"},
{name = "id", type = "integer", sortOrder = "asc"}
])>

在sortArrayOfStructsUsingQuery函数中,我只根据传入的键构造一个查询,然后对该查询进行排序。然后,我遍历查询,从数组中找到与当前查询行的数据匹配的结构元素,并将该结构添加到我交回的数组中。

完全有可能在这段代码中有一个漏洞,我的测试没有发现(我还没有很多用例),但是如果它对任何人都有用,那么就是这样。希望它有用,如果有任何明显的漏洞,我很高兴听到它们。

(只是一个注释:我对所有将留在函数中的变量使用“本地”范围,对于我打算交回的任何内容使用“r”范围,无论价值多少)

<cffunction name="sortArrayOfStructsUsingQuery" output="yes" returnType="array">
<cfargument name="array" type="array" required="true">
<cfargument name="sortKeys" type="array" required="true">

<cfset var local = {
    order = {
        keyList = "",
        typeList = "",
        clause = ""
    },
    array = duplicate(arguments.array),
    newArray = []
}>

<cfset var r = {
    array = []
}>

<cftry>

    <!--- build necessary lists out of given sortKeys array --->
    <cfloop array=#arguments.sortKeys# index="local.key">
        <cfset local.order.keyList = listAppend(local.order.keyList, local.key.name)>
        <cfset local.order.typeList = listAppend(local.order.typeList, local.key.type)>
        <cfset local.order.clause = listAppend(local.order.clause, "#local.key.name# #local.key.sortOrder#")>
    </cfloop>


    <!--- build query of the relevant sortKeys --->
    <cfset local.query = queryNew(local.order.keyList, local.order.typeList)>   
    <cfloop array=#arguments.array# index="local.obj">
        <cfset queryAddRow(local.query)>
        <cfloop list=#local.order.keyList# index="local.key">
            <cfset querySetCell(local.query, local.key, structFind(local.obj, local.key))>
        </cfloop>
    </cfloop>

    <!--- sort the query according to keys --->
    <cfquery name="local.sortedQuery" dbtype="query">
        SELECT *
          FROM [local].query
         ORDER BY #local.order.clause#
    </cfquery>

    <!--- rebuild the array based on the sorted query, then hand the sorted array back --->
    <cfloop query="local.sortedQuery">
        <cfloop from=1 to=#arraylen(local.array)# index=local.i>

            <cfset local.matchP = true>
            <cfloop list=#local.order.keylist# index="local.key">
                <cfif structKeyExists(local.array[local.i], local.key)
                  AND structFind(local.array[local.i], local.key) EQ evaluate("local.sortedQuery.#local.key#")>
                      <cfset local.matchP = true>
                <cfelse>
                    <cfset local.matchP = false>
                    <cfbreak>
                </cfif>
            </cfloop>      

            <cfif local.matchP>
                <cfset arrayAppend(r.array, local.array[local.i])>
            <cfelse>
                <cfif NOT arrayContains(local.newArray, local.array[local.i])>
                    <cfset arrayAppend(local.newArray, local.array[local.i])>
                </cfif>
            </cfif>

        </cfloop>

        <cfset local.array = local.newArray>

    </cfloop>

    <!--- Outbound array should contain the same number of elements as inbound array --->
    <cfif arrayLen(r.array) NEQ arrayLen(arguments.array)>
        <!--- log an error here --->
        <cfset r.array = arguments.array>
    </cfif>

<cfcatch type="any">
            <!--- log an error here --->
    <cfset r.array = arguments.array>
</cfcatch>

</cftry>

<cfreturn r.array>

</cffunction>

答案 5 :(得分:2)

使用新的CF Closure支持实际上更容易。

这是我今天工作的一个例子,我希望通过结构中存储的日期对结构数组进行排序。我按降序排序。

ArraySort(yourArrayOfStructs, function(a,b) {
    if ( DateCompare(a.struct_date, b.struct_date) == -1 ) {
        return true;
    } else {
        return false;
    }
});

我无法从2012年的Ray Camden's封装中获得完全的信用。

答案 6 :(得分:2)

这是一个基于Tomalak的答案的UDF,它也支持自定义对象(例如,某些基于Railo的CMS使用)。此功能与ColdFusion 9兼容。

<cffunction name="sortStructArray" 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 _sct = StructNew()>
  <cfset var _aryKeys = ArrayNew(1)>
  <cfset var arySorted = ArrayNew(1)>
  <cfif IsStruct(base[1])>
    <!--- Standard structure --->
    <cfloop from="1" to="#ArrayLen(base)#" index="i">
      <cfset _sct[i] = base[i]>
    </cfloop>
    <cfset _aryKeys = StructSort(_sct, sortType, sortOrder, pathToSubElement)>
    <cfloop from="1" to="#ArrayLen(_aryKeys)#" index="i">
      <cfset arySorted[i] = _sct[_aryKeys[i]]>
    </cfloop>
  <cfelse>
    <!--- Custom object (e.g., Catalog) --->
    <cfloop from="1" to="#ArrayLen(base)#" index="i">
      <cfset _sct[i] = StructNew()>
      <cfset _sct[i][pathToSubElement] = base[i][pathToSubElement]>
    </cfloop>
    <cfset _aryKeys = StructSort(_sct, sortType, sortOrder, pathToSubElement)>
    <cfloop from="1" to="#ArrayLen(_aryKeys)#" index="i">
      <cfset arySorted[i] = base[_aryKeys[i]]>
    </cfloop>
  </cfif>
  <cfreturn arySorted>
</cffunction>

答案 7 :(得分:2)

我没有在上面的@ mikest34帖子上发表评论的声誉点,但@russ是正确的,这个回调不再按照解释的方式工作。

Adam Cameron发现当使用带回调的arraySort时,它不再需要True / False响应,而是:

  

-1,如果第一个参数比第二个参数小“    0,如果第一个参数等于第二个参数
   1,第一个参数比第二个参数“更大”

所以正确的回调是:

ArraySort(yourArrayOfStructs, function(a,b) {
    return compare(a.struct_date, b.struct_date);
});

在CF2016中测试和工作

答案 8 :(得分:1)

如果您不想使用自定义方法,Coldfusion具有 structSort 方法http://www.cfquickdocs.com/cf8/#StructSort。是的,它使用嵌套结构对结构进行排序,BUT返回数组,因此可用于实现相同的结果。

答案 9 :(得分:0)

使用arraySort回调使用多个键对结构数组进行排序的简便解决方案:

将结构体数组作为第一个参数进行排序,将结构体数组以sortkey / sortorder对的格式作为第二个参数,例如[{sortkey:'FirstName',sortorder:'asc'},{sortkey:'LastName',sortorder:'desc'}]。

<cffunction name="arrayOfStructsSort" access="public" returntype="array" output="false" hint="This sorts an array of structures.">
    <cfargument name="aOfS" type="array" required="yes" />
    <cfargument name="key_sortOrder" type="array" required="yes" />
    <cfscript>

    arraySort(
        aOfS,
        function (a, b) {
            for (var i = 1; i lte arrayLen(key_sortOrder); i = i + 1) {
                var prop = key_sortOrder[i];
                var key = prop.key;
                var sortOrder = prop.sortOrder;
                if (a[key] lt b[key]) {
                    if (sortOrder eq 'desc') {
                        return 1;
                    } else {
                        return -1;
                    }
                }
                if (a[key] gt b[key]) {
                    if (sortOrder eq 'desc') {
                        return -1;
                    } else {
                        return 1;
                    }
                }
            }
            return 0;
        }
    );

    return aOfS;

    </cfscript>

</cffunction>

只需调用:

<cfset ArraySorted = arrayOfStructsSort(arrayToBeSorted,arrayOfSorkeys)>