quería tirar mis dos centavos aquí. Me encontré con un caso en el que necesitaba ordenar una matriz de estructuras usando más de una clave. Terminé usando una consulta construida para hacer mi clasificación. La función toma el conjunto de estructuras como primer argumento, y luego una serie de estructuras que indican el orden de clasificación, así:
<cfset result = sortArrayOfStructsUsingQuery(myArrayOfStructs,[
{name = "price", type = "decimal", sortOrder = "asc"},
{name = "id", type = "integer", sortOrder = "asc"}
])>
Dentro de la función sortArrayOfStructsUsingQuery, construyo una consulta basada sólo en las claves que pase en , luego ordena esa consulta Luego, recorro la consulta, encuentro el elemento de estructura de la matriz que coincide con los datos en la fila de consulta actual y agrego esa estructura a la matriz que devuelvo.
Es muy posible que haya un agujero en este código que mis pruebas no han descubierto (no ha habido muchos casos de uso para mí todavía), pero en caso de que sea útil para cualquiera, aquí está. Espero que sea útil, y si hay algún agujero evidente, me alegra saber de ellos.
(sólo una nota: Yo uso el ámbito "local" para todas las variables que se quedará en la función, y la "r" margen para nada pretendo a devolver, por lo que vale la pena)
<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>
"claves" es necesario que haya var con ámbito, creo. –
@Edward: Absolutamente, me he perdido esa. Gracias por la pista. – Tomalak
Muchas de las otras respuestas aquí dependen de la función de devolución de llamada arraySort() (agregada en CF10) o función de miembro sort() (agregada en CF11). La respuesta de Tomalak funciona al menos hasta CF9, que todavía tengo que admitir. ¡Gracias, Tomalak! –