php - multidimensional - Ordenar matriz de matrices multidiminsionales en más de una "columna"(clave) con opciones de clasificación especificadas
usort php (4)
Estoy buscando poder ordenar una matriz de matrices multidimensionales en más de una columna. Para complicarlo aún más, me gustaría poder establecer opciones de clasificación específicas por clave / columna. Tengo lo que es similar al resultado de una consulta DB, pero en realidad no proviene de uno, por lo tanto, la necesidad de ordenarlo en PHP en lugar de SQL.
Array
(
[0] => Array
(
[first_name] => Homer
[last_name] => Simpson
[city] => Springfield
[state] => Unknown
[zip] => 66735
)
[1] => Array
(
[first_name] => Patty
[last_name] => Bouvier
[city] => Scottsdale
[state] => Arizona
[zip] => 85250
)
[2] => Array
(
[first_name] => Moe
[last_name] => Szyslak
[city] => Scottsdale
[state] => Arizona
[zip] => 85255
)
[3] => Array
(
[first_name] => Nick
[last_name] => Riviera
[city] => Scottsdale
[state] => Arizona
[zip] => 85255
)
)
Me gustaría poder ordenarlo de forma similar a lo que se podría hacer con una consulta DB. Ah, y a veces una columna / clave debe especificarse por número.
Lo que tenía en mente era algo similar a esto:
$sortOptions = array( array( ''city'', SORT_ASC, SORT_STRING ),
array( ''zip'', SORT_DESC, SORT_NUMERIC),
array( 2, SORT_ASC, SORT_STRING) // 2=''last_name''
);
$sorter = new MultiSort($data, $sortOptions );
$sortedData = $sorter->getSortedArray() ;
print_r( $jmsSorted);
Lo que me gustaría terminar es esto:
Array
(
[0] => Array
(
[first_name] => Nick
[last_name] => Riviera
[city] => Scottsdale
[state] => Arizona
[zip] => 85255
)
[1] => Array
(
[first_name] => Moe
[last_name] => Szyslak
[city] => Scottsdale
[state] => Arizona
[zip] => 85255
)
[2] => Array
(
[first_name] => Patty
[last_name] => Bouvier
[city] => Scottsdale
[state] => Arizona
[zip] => 85250
)
[3] => Array
(
[first_name] => Homer
[last_name] => Simpson
[city] => Springfield
[state] => Unknown
[zip] => 66735
)
)
ACTUALIZACIÓN: Creo que idealmente, una solución daría lugar a la creación dinámica
array_multisort( $city, SORT_ASC, SORT_STRING, $zip, SORT_DESC, SORT_NUMERIC, $last_name, SORT_ASC, SORT_STRING, $inputArray);
El problema es que no quiero tener que "codificar" esos nombres clave allí. Traté de crear una solución basada en el Ejemplo # 3 Ordenar los resultados de la base de datos de la documentación array_multisort()
que terminó usando array_multisort()
pero parece que no puedo encontrar una manera de usar mi lista de argumentos construida dinámicamente para array_multisort()
.
Mi intento fue "encadenar" esos argumentos en una matriz y luego
call_user_func_array( ''array_multisort'', $functionArgs);
Eso resulta en un
Warning: Parameter 2 to array_multisort() expected to be a reference, value given in...
Es posible que desee probar el uso de usort . Todo lo que tiene que hacer es realizar funciones que le digan al clasificador cómo ordenarlas. Los documentos tienen más información sobre cómo hacer eso.
Esto debería funcionar para la situación que describes.
usort($arrayToSort, "sortCustom");
function sortCustom($a, $b)
{
$cityComp = strcmp($a[''city''],$b[''city'']);
if($cityComp == 0)
{
//Cities are equal. Compare zips.
$zipComp = strcmp($a[''zip''],$b[''zip'']);
if($zipComp == 0)
{
//Zips are equal. Compare last names.
return strcmp($a[''last_name''],$b[''last_name'']);
}
else
{
//Zips are not equal. Return the difference.
return $zipComp;
}
}
else
{
//Cities are not equal. Return the difference.
return $cityComp;
}
}
Podrías condensarlo en una línea así:
function sortCustom($a, $b)
{
return ($cityComp = strcmp($a[''city''],$b[''city'']) ? $cityComp : ($zipComp = strcmp($a[''zip''],$b[''zip'']) ? $zipComp : strcmp($a[''last_name''],$b[''last_name''])));
}
En cuanto a tener una función de clasificación personalizable, estás reinventando la rueda. Eche un vistazo a la función array_multisort()
.
En PHP 5.3, cada parámetro en la matriz debe ser una referencia cuando se llama a array_multisort()
con call_user_func_array()
.
Esta función ordena una matriz multidimensional y muestra una forma de construir una matriz de parámetros referenciados que funciona correctamente.
function msort()
{
$params = func_get_args();
$array = array_pop($params);
if (!is_array($array))
return false;
$multisort_params = array();
foreach ($params as $i => $param)
{
if (is_string($param))
{
${"param_$i"} = array();
foreach ($array as $index => $row)
{
${"param_$i"}[$index] = $row[$param];
}
}
else
${"param_$i"} = $params[$i];
$multisort_params[] = &${"param_$i"};
}
$multisort_params[] = &$array;
call_user_func_array("array_multisort", $multisort_params);
return $array;
}
Ejemplo:
$ data es la matriz dada de la pregunta
$sorted_data = msort(''city'', SORT_ASC, SORT_STRING, ''zip'', SORT_DESC, SORT_NUMERIC, $data)
Esto es lo que finalmente decidí por poder ordenar matrices multidimensionales. Ambas respuestas son buenas pero también buscaba algo flexible.
Definitivamente no creo que haya una respuesta "correcta", pero esto es lo que funciona para mis necesidades y es flexible.
Como puede ver en mi @link
en el comentario de _usortByMultipleKeys()
fue adaptado de un comentario en el manual de PHP que actualmente no parece existir, pero creo que http://www.php.net/manual/en /function.usort.php#104398 es una nueva versión del comentario original. No he explorado usando esa nueva sugerencia.
/**
* Sort the resultSet.
*
* Usage: $sortOptions = array(
* ''section'', // Defaults to SORT_ASC
* ''row'' => SORT_DESC,
* ''retail_price'' => SORT_ASC);
* $results->sortResults($sortOptions);
*
* @param array $sortOptions An array of sorting instructions
*/
public function sortResults(array $sortOptions)
{
usort($this->_results, $this->_usortByMultipleKeys($sortOptions));
}
/**
* Used by sortResults()
*
* @link http://www.php.net/manual/en/function.usort.php#103722
*/
protected function _usortByMultipleKeys($key, $direction=SORT_ASC)
{
$sortFlags = array(SORT_ASC, SORT_DESC);
if (!in_array($direction, $sortFlags)) {
throw new InvalidArgumentException(''Sort flag only accepts SORT_ASC or SORT_DESC'');
}
return function($a, $b) use ($key, $direction, $sortFlags) {
if (!is_array($key)) { //just one key and sort direction
if (!isset($a->$key) || !isset($b->$key)) {
throw new Exception(''Attempting to sort on non-existent keys'');
}
if ($a->$key == $b->$key) {
return 0;
}
return ($direction==SORT_ASC xor $a->$key < $b->$key) ? 1 : -1;
} else { //using multiple keys for sort and sub-sort
foreach ($key as $subKey => $subAsc) {
//array can come as ''sort_key''=>SORT_ASC|SORT_DESC or just ''sort_key'', so need to detect which
if (!in_array($subAsc, $sortFlags)) {
$subKey = $subAsc;
$subAsc = $direction;
}
//just like above, except ''continue'' in place of return 0
if (!isset($a->$subKey) || !isset($b->$subKey)) {
throw new Exception(''Attempting to sort on non-existent keys'');
}
if ($a->$subKey == $b->$subKey) {
continue;
}
return ($subAsc==SORT_ASC xor $a->$subKey < $b->$subKey) ? 1 : -1;
}
return 0;
}
};
}