Как отсортировать массив структур в ColdFusion
У меня есть массив структур в ColdFusion. Я хотел бы отсортировать этот массив на основе одного из атрибутов в структурах. Как я могу это достичь? Я нашел функцию StructSort, но она принимает структуру, и у меня есть массив.
Если это невозможно в ColdFusion, возможно ли это в Java (возможно, используя Arrays.sort(Object[], Comparator)
)?
Ответы
Ответ 1
Как обычно, 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 ([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;
}
Ответ 2
Вот что-то, что очень напоминает оригинальный 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]()
Ответ 3
Принятое решение (из CFLib.org) НЕ безопасно. Я экспериментировал с этим для чего-то, что мне нужно было сделать на работе, и обнаружил, что он возвращает неверные результаты при сортировке числовых чисел с помощью float.
Например, если у меня есть эти структуры: (псевдокод)
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');
Перейдите по отсортированному массиву и напечатайте имя и вес.
Это не будет в правильном порядке, и это ограничение смешивания
произвольный ключ со отсортированным значением.
Ответ 4
Вы можете использовать библиотеку Underscore.cfc, чтобы выполнить то, что вы хотите:
arrayOfStructs = [
{myAttribute: 10},
{myAttribute: 30},
{myAttribute: 20}
];
_ = new Underscore();
sortedArray = _.sortBy(arrayOfStructs, function (struct) {
return struct.myAttribute;
});
Underscore.cfc позволяет определить пользовательский компаратор и делегировать команду arraySort(). Вы можете использовать его для сортировки массивов, структур, запросов или строковых списков, но он всегда возвращает массив.
(Отказ от ответственности: я написал Underscore.cfc)
Ответ 5
Я хотел бросить здесь свои два цента. Я столкнулся с ситуацией, когда мне нужно было отсортировать массив структур, используя более одного ключа. Я закончил с помощью построенного запроса, чтобы выполнить сортировку. Функция принимает массив структур как первый аргумент, а затем массив структур, указывающий порядок сортировки, например:
<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>
Ответ 6
Здесь используется UDF на основе ответа Tomalak, который также поддерживает пользовательские объекты (например, используемые некоторыми CMS на основе Railo). Эта функция совместима с 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
Если вы не хотите использовать пользовательские методы, Coldfusion имеет structSort метод http://www.cfquickdocs.com/cf8/#StructSort. Да, он сортирует структуру с вложенными структурами, BUT возвращает массив, поэтому его можно использовать для достижения того же результата.
Ответ 8
На самом деле это еще проще с новой поддержкой CF Closure.
Вот пример, с которым я работал сегодня, когда хотел отсортировать массив структур по дате, хранящейся в структуре. Я сортировал в порядке убывания.
ArraySort(yourArrayOfStructs, function(a,b) {
if ( DateCompare(a.struct_date, b.struct_date) == -1 ) {
return true;
} else {
return false;
}
});
Я не могу взять общий кредит, поскольку я адаптировал это из Ray Camden на Closures с 2012 года.
Ответ 9
У меня нет точек репутации для комментариев в сообщении @mikest34 выше, но @russ был прав, что этот обратный вызов больше не работает так, как он был объяснен.
Именно Адам Кэмерон обнаружил, что при использовании arraySort с обратным вызовом он больше не требует ответа True/False, а скорее:
-1, если первый параметр "меньше", чем второй параметр
0, если первый параметр равен второму параметру
1, первый параметр "больше", чем второй параметр
Таким образом, правильный обратный вызов:
ArraySort(yourArrayOfStructs, function(a,b) {
return compare(a.struct_date, b.struct_date);
});
Тестирование и работа в CF2016