2010-04-16 14 views
17

Ich habe ein Array von Strukturen in ColdFusion. Ich möchte dieses Array basierend auf einem der Attribute in den Strukturen sortieren. Wie kann ich das erreichen? Ich habe die StructSort Funktion gefunden, aber es braucht eine Struktur und ich habe ein Array.Wie sortiere ich ein Array von Strukturen in ColdFusion

Wenn dies nicht rein in ColdFusion möglich ist, ist es irgendwie in Java möglich (vielleicht mit Arrays.sort(Object[], Comparator))?

Antwort

12

Wie immer hat CFLib.org genau das, was Sie wollen.

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 ([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; 
} 
8

Hier ist etwas, das ähnelt der ursprüngliche StructSort(). Es unterstützt auch das Argument 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> 

Verwendung/Test:

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

Ergebnis:

ArrayOfStructSort Result

+0

"Schlüssel" muss var-scoped sein, glaube ich. –

+0

@Edward: Absolut, das habe ich vermisst. Danke für den Tipp. – Tomalak

+1

Viele der anderen Antworten hängen hier von der arraySort() - Callback-Funktion (hinzugefügt in CF10) oder der sort() - Member-Funktion ab (hinzugefügt in CF11). Tomalaks Antwort funktioniert zumindest zurück zu CF9, die ich noch unterstützen muss. Danke, Tomalak! –

1

Falls Sie nicht wollen, benutzerdefinierte Methoden bedienen, hat Coldfusion structSort Methode http://www.cfquickdocs.com/cf8/#StructSort. Ja, es sortiert die Struktur mit verschachtelten Strukturen, ABER gibt das Array zurück, damit dasselbe Ergebnis erzielt werden kann.

+3

Wie würden Sie 'structSort()' verwenden, um ein Array von Strukturen zu sortieren? – 10basetom

5

Die akzeptierte Lösung (von CFLib.org) ist NICHT sicher. Ich experimentierte mit diesem für etwas, das ich bei der Arbeit tun musste und fand, dass es falsche Ergebnisse zurückgibt, wenn man numerisch mit Hin- und Herbewegungen sortiert.

Zum Beispiel, wenn ich diese Strukturen: (Pseudo-Code)


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 über sortierten Array und den Namen & Gewicht drucken. Es wird nicht in der richtigen Reihenfolge sein, und dies ist eine Beschränkung der Mischung ein beliebiger Schlüssel mit dem Wert sortiert wird.

+4

Gute Informationen zu teilen, aber da Sie keine alternative Lösung vorschlagen, sollte dies in einem Kommentar zu dieser Antwort sein. Sie könnten das Codebeispiel in ein gist/pastebin/etc einfügen, damit es passt. –

4

Sie können die Underscore.cfc library verwenden, um zu erreichen, was Sie wollen:

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

_ = new Underscore(); 

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

Underscore.cfc können Sie einen benutzerdefinierten Komparator und Delegierten ArraySort() definieren. Sie können es zum Sortieren von Arrays, Structs, Abfragen oder String-Listen verwenden, es wird jedoch immer ein Array zurückgegeben.

(Disclaimer: Ich schrieb Underscore.cfc)

2

wollte ich hier meine zwei Cent werfen. Ich stieß auf einen Fall, in dem ich ein Array von Strukturen mit mehr als einem Schlüssel sortieren musste. Ich endete mit einer konstruierten Abfrage, um meine Sortierung durchzuführen. Die Funktion nimmt das Array von Strukturen als erstes Argument, und dann eine Reihe von structs die Reihenfolge angibt, wie folgt aus:

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

Innerhalb der sortArrayOfStructsUsingQuery Funktion Konstrukt I einer Abfrage basiert nur auf den Tasten I passieren in , dann sortiere diese Abfrage. Dann wiederhole ich die Abfrage, finde das Strukturelement aus dem Array, das mit den Daten in der aktuellen Abfragezeile übereinstimmt, und füge diese Struktur dem Array hinzu, das ich zurückgebe.

Es ist durchaus möglich, dass es in diesem Code ein klaffendes Loch gibt, das meine Tests nicht aufgedeckt haben (es gab noch nicht viele Anwendungsfälle für mich), aber falls es für irgendjemanden nützlich ist, hier ist es. Ich hoffe, es ist nützlich, und wenn es irgendwelche grellen Löcher gibt, bin ich glücklich, von ihnen zu hören.

(nur eine Anmerkung: Ich benutze den „lokalen“ Spielraum für alle Variablen, die in der Funktion bleiben, und das „r“ Raum für alles, was ich beabsichtige, zurück zu geben, denn was es wert ist)

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

Mit der neuen CF-Closure-Unterstützung ist es sogar noch einfacher.

Hier ist ein Beispiel, an dem ich heute gearbeitet habe, wo ich ein Array von Strukturen nach einem in der Struktur gespeicherten Datum sortieren wollte. Ich sortierte in absteigender Reihenfolge.

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

Ich kann nicht insgesamt Kredit nehmen, wie ich auf Closures diese von Ray Camden angepasst ab 2012

+0

Oder Funktion (a, b) {return (a.struct_date

+0

ist das nur in CF 10? – Kip

+1

Inline-Funktionsausdrücke und -schließungen wurden mit CF10 und Railo 4.0 hinzugefügt, ebenso wie der aktualisierte ArraySort. Sie konnten UDF immer als Argumente übergeben, aber keine der integrierten Funktionen hatte Argumente, die zuvor Funktionen akzeptiert hatten. Sie erlauben immer noch (derzeit) keine BIFs, aber das wird sich hoffentlich in der nächsten Version ändern. –

2

Hier ist ein UDF basierend auf Tomalak Antwort, die auch benutzerdefinierte Objekte unterstützt (zB verwendet von einigen Railo- basierte CMS). Diese Funktion ist kompatibel mit 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> 
+0

Nice one. Ich wollte gerade in meine eigene Antwort schauen, aber ich schätze, ich kann das jetzt ein wenig verzögern ... – Tomalak

2

ich zu kommentieren, nicht die Rating-Punkte haben @ mikest34 Beitrag oben aber @russ war richtig, dass dieser Rückruf nicht mehr so ​​funktioniert, wurde erläutert.

Es war Adam Cameron, der entdeckt, dass, wenn mit einem Rückruf mit ArraySort, es benötigt nicht länger eine Wahr/Falsch-Antwort, sondern vielmehr:

-1, wenn der erste Parameter „kleiner“ als der zweite Parameter ist , wenn die erste Parameter auf dem zweiten Parameter gleich
1, erster Parameter ist "größer" als zweiter Parameter

So der richtige Rückruf:

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

Testen und Arbeiten in CF2016

Verwandte Themen