Je voulais jeter mes deux cents ici. J'ai rencontré un cas où j'avais besoin de trier un tableau de structures en utilisant plus d'une clé. J'ai fini par utiliser une requête construite pour faire mon tri. La fonction prend le tableau de struct comme premier argument, puis un tableau de struct indiquant l'ordre de tri, comme ceci:
<cfset result = sortArrayOfStructsUsingQuery(myArrayOfStructs,[
{name = "price", type = "decimal", sortOrder = "asc"},
{name = "id", type = "integer", sortOrder = "asc"}
])>
Au sein de la fonction sortArrayOfStructsUsingQuery, construire une requête basée uniquement sur les touches je passe , puis triez cette requête. Ensuite, je boucle sur la requête, trouve l'élément de structure du tableau qui correspond aux données de la ligne de requête en cours, et ajoute cette structure au tableau que je remets.
Il est tout à fait possible qu'il y ait un trou béant dans ce code que mon test n'a pas découvert (il n'y a pas encore eu beaucoup de cas d'utilisation), mais c'est utile. J'espère que c'est utile, et s'il y a des trous flagrants, je suis heureux d'entendre parler d'eux.
(juste une note: J'utilise le champ « local » pour toutes les variables qui vont rester dans la fonction, et le « r » champ d'application pour tout ce que je l'intention de restituer, pour tout ce que ça vaut)
<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>
« clés » doit être var scope, je crois. –
@Edward: Absolument, j'ai raté celui-là. Merci pour l'indice. – Tomalak
La plupart des autres réponses dépendent de la fonction de rappel arraySort() (ajoutée dans CF10) ou de la fonction de membre sort() (ajoutée dans CF11). La réponse de Tomalak fonctionne au moins jusqu'à CF9, que je dois encore soutenir. Merci, Tomalak! –