【问题标题】:How to sort an array of structs in ColdFusion如何在 ColdFusion 中对结构数组进行排序
【发布时间】:2011-02-08 21:00:08
【问题描述】:

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

如果这在 ColdFusion 中是不可能的,那么在 Java 中是否有可能(也许使用Arrays.sort(Object[], Comparator))?

【问题讨论】:

    标签: sorting coldfusion struct


    【解决方案1】:

    这是与原始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>
    

    结果:

    【讨论】:

    • "keys" 需要是 var-scoped,我相信。
    • @Edward:当然,我错过了那个。感谢您的提示。
    • 这里的许多其他答案取决于 arraySort() 回调功能(在 CF10 中添加)或 sort() 成员函数(在 CF11 中添加)。 Tomalak 的回答至少可以追溯到 CF9,我仍然必须支持。谢谢你,托马拉克!
    【解决方案2】:

    像往常一样,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;
    }
    

    【讨论】:

      【解决方案3】:

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

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

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

      所以正确的回调是:

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

      在 CF2016 中测试和工作

      【讨论】:

      • 谢谢!对我来说,我正在对查询结果进行排序,所以结果是 result.sort(function(a,b){....});
      【解决方案4】:

      接受的解决方案(来自 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');

      遍历排序后的数组并打印名称和权重。 它不会按正确的顺序,这是混合的限制 具有正在排序的值的任意键。

      【讨论】:

      • 分享的好信息,但由于您没有提出替代解决方案,因此应该在对该答案的评论中。您可以将代码示例放入 gist/pastebin/etc 以使其适合。
      【解决方案5】:

      您可以使用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)

      【讨论】:

        【解决方案6】:

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

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

        【讨论】:

          【解决方案7】:

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

          这是我今天处理的一个示例,我想按存储在结构中的日期对结构数组进行排序。我是按降序排序的。

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

          我不能完全相信这是我从 2012 年 Ray Camden 关于闭包的文章中改编而来的。

          【讨论】:

          • function(a,b){ return ( a.struct_date &lt; b.struct_date ); }
          • 这仅在 CF 10 中吗?
          • CF10 和 Railo 4.0 添加了内联函数表达式和闭包,更新的 ArraySort 也是如此。您始终能够将 UDF 作为参数传递,但没有一个内置函数具有以前接受函数的参数。他们(目前)仍然不允许 BIF,但希望在下一个版本中会有所改变。
          • 谢谢彼得。发布后我也意识到有更简单的方法。
          • 注意arraySort() 的实现可能已经改变:cfmlblog.adamcameron.me/2013/07/…
          【解决方案8】:

          这是一个基于 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>
          

          【讨论】:

          • 不错的一个。我正要研究自己的答案,但我想我现在可以推迟一下......
          【解决方案9】:

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

          【讨论】:

          • 如何使用structSort() 对结构数组进行排序?
          【解决方案10】:

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

          它将要排序的结构数组作为第一个参数,并将排序键/排序顺序对格式的结构数组作为第二个参数,例如[{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)>
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-09-06
            • 2019-11-27
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多