Ordenar y desordenar matrices en PHP

PHP nos ofrece importantes funcionalidades a la hora de iterar el orden de los elementos de una matriz. En este tutorial veremos la operativa de las funciones al efecto, y su comportamiento con respecto a los valores y las claves.

La ordenación básica en una matriz en PHP

La ordenación mas elemental que se puede llevar a cabo en una matriz es sobre el valor de los elementos de la misma, mediante la funcion sort() que recibe, como argumento, el nombre de la matriz a ordenar. Vea el siguiente fragmento:

$matriz = array("a"=>"gris", "b"=>"rojo", "c"=>"azul", "d"=>"blanco");
sort($matriz);
var_dump($matriz);

Antes de ver el resultado, observe, cómo usamos la función. Esta no devuelve la matriz ordenada a una salida (si así fuera, habríamos escrito $matriz = sort($matriz);. En su lugar, opera, directamente, sobre la matriz que recibe como argumento. El resultado de este script es el siguiente:

array(4){
[0]=>
string(4) "azul"
[1]=>
string(6) "blanco"
[2]=>
string(4) "gris"
[3]=>
string(4) "rojo"
}

Vea que se han ordenado los elementos en base a sus valores… perdiéndose las claves y, por lo tanto, las paridades clave-valor. Si queremos mantener dichas paridades deberemos usar, en su lugar, la función asort(), así:

$matriz = array("a"=>"gris", "b"=>"rojo", "c"=>"azul", "d"=>"blanco");
asort($matriz);
var_dump($matriz);

Ahora, el resultado es el siguiente:

array(4) {
["c"]=>
string(4) "azul"
["d"]=>
string(6) "blanco"
["a"]=>
string(4) "gris"
["b"]=>
string(4) "rojo"
}

Como ve, cada elemento mantiene la paridad clave-valor. La ordenación se ha hecho en base a los valores, al igual que en el caso de sort().

Estas dos fuciones admiten un segundo parámetro, que puede ser una de las siguientes constantes:

  • SORT_REGULAR. Es el valor asumido por defecto. Significa que los elementos son tenidos en cuenta, para su ordenación, sin ninguna transformación previa, tal como aparecen en la matriz.
  • SORT_NUMERIC. Convierte el valor de cada elemento en un valor numérico para su ordenación.
  • SORT_STRING. Convierte el valor de cada elemento en una cadena para su ordenación.

Las conversiones a las que se refieren SORT_NUMERIC y SORT_STRING las hace PHP, internamente, sobre una copia de la matriz, con la que opera. Al obtener los resultados, los valores de los elementos no han sido modificados.

La ordenación realizada por sort() y asort() se llevan a cabo en orden ascendente (de menor a mayor). Para la ordenación inversa, use las funciones rsort() y arsort(), respectivamente.

Si desea que la ordenación se lleve a cabo en base a las claves de los elementos, y no en base a sus valores, emplee las funciones ksort() (para ordenaciones ascendentes) y krsort() (para ordenaciones descendentes). Observe el siguiente ejemplo:

$matriz = array(2=>"rojo", 7=>"azul", 1=>"blanco");
ksort($matriz);
var_dump($matriz);

El resultado obtenido será el que aparece a continuación:

array(4) {
[1]=>
string(6) "blanco"
[2]=>
string(4) "rojo"
[7]=>
string(4) "azul"
}

Observe que ksort(), a diferencia de sort(), mantine las paridades clave-valor. Lo mismo ocurre con krsort(), con respecto a rsort().

La ordenación natural de las matrices en PHP

La ordenación de los elementos de una matriz no siempre sigue un criterio natural. Por ejemplo, suponga lo siguiente:

$matriz = array("8a", "22a", "15a", "3a", "1a");
sort($matriz);
var_dump($matriz);

El resultado de este fragmento es el que aparece a continuación:

array(5) {
[0]=>
string(3) "15a"
[1]=>
string(2) "1a!
[2]=>
string(3) "22a"
[3]=>
string(2) "3a"
[4]=>
string(2) "8a"
}

Como ve, no tiene mucho sentido, desde el punto de vista del usuario humano, que el elemento cuyo valor es 15a aparezca, por ejemplo, antes de 1a o que 22a lo haga antes de 3a. Esto se debe a que la ordenación toma, como criterio, el código ASCII. Para realizar una ordenación «natural» de los elementos, contamos con la función natsort().

Si en nuesto último ejemplo sustituimos sort() por esta, obtendremos el siguiente resultado:

array(5) {
[4]=>
string(2) "1a"
[3]=>
string(2) "3a"
[0]=>
string(2) "8a"
[2]=>
string(3) "15a"
[1]=>
string(3) "22a"
}

Como ve, es mucho más legible por humanos que el anterior. Además, esta función respeta las paridades clave-valor de los elementos. Si desea que la ordenación sea insensible a la capitalización de los valores, emplee natcasesort().

Ordenaciones personalizadas de las matrices en PHP

En ocaciones es necesario ordenar los elementos de una matriz siguiento un criterio especial definido por el programador para lograr un resultado específico. Lo que hacemos es definir una función de usuario y mediante la función usort() le indicamos al intérprete que ordene la matriz según dicha función de usuario. La función de usuario se basa en comparaciones de los elementos de la matriz, tomados de dos en dos. En base al resultado de esta comparación, la función de usuario deberá devolver un 0 (si son iguales), un valor negativo (si el primer elemento es menor que el segundo) o un valor positivo (si el primer elemento es mayor que el segundo). Será usort() quien se encargue de determinar en qué orden pasan los elementos de la matriz a la función y, en base al resultado de esta, actuar sobre ella. Empecemos por ver un ejemplo:

function ordenar($elemento_A, $elemento_B){
if($elemento_A == $elemento_B) return 0;
return ($elemento_A < $elemento_B)? -1 : 1;
}
$matriz = array(3, 2, 5, 6, 1);
usort($matriz, "ordenar");
var_dump($matriz);

Al ejecutar este script obtendremos el siguiente resultado:

array(5) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
[3]=>
int(5)
[4]=>
int(6)
}

Como ve, este ejemplo no es muy interesante, desde el punto de vista práctico. Lo cierto es que, para obtener este resultado, habría bastado con usar, directamente,la función sort(). Pero podemos afinar mucho más. Suponga que tiene una matriz con los nombres de algunas provincias de España y necesita que aparezcan en orden alfabético, pero siempre Madrid debe aparecer en último lugar. No existe ninguna función nativa de PHP que realice una ordenación así. Debemos crear una función a medida y emprear usort() para que PHP la aplique sobre la matriz, así:

function ordenar($elmento_A, $elemento_B){
if($elemento_A == $elemento_B)}
return 0;
if($elemento_A < $elemento_B && $elemento_A != "MADRID"){
return -1;
} else {
return 1;
}
}
$matriz = array("CADIZ", "CUENTA", "MADRID", "ZAMORA", "SEVILLA", "BARCELONA");
usort($matriz, "ordenar");
var_dump($matriz);

El resultado, al ejecutar este script, será el siguiente:

array(6) {
[0]=>
string(9) "BARCELONA"
[1]=>
string(5) "CADIZ"
[2]=>
string(6) "CUENCA"
[3]=>
string(7) "SEVILLA"
[4]=>
string(6) "ZAMORA"
[5]=>
string(6) "MADRID"
}

A partir de aquí, se trata de experimentar con la función de usuario, y de complementar el uso de usort() con otras técnicas, para lograr los resultados deseados en cada caso. Por ejemplo, si en el caso de este último script, necesitamos que MADRID aparezca en primer lugar, en vez de al final, usaremos las técnicas de extracción e implantación de elementos.

Por supuesto, usort() puede emplearse, también, para trabajar con matrices profundas, tal como muestra el siguiente ejemplo:

function cmp($a, $b){
return strcmp($a["fruta"], $b["fruta"]);
}
$frutas[0]["fruta"]="limones";
$frutas[1]["fruta"]="bananos";
$frutas[2]["fruta"]="granadillas";
usort($frutas, "cmp");
var_dump($frutas);

El resultado será el siguiente:

array(3) {
[0]=>
array(1) {
["fruta"]=>
string(7) "bananos"
}
[1]=>
array(1) {
["fruta"]=>
string(11) "granadillas"
}
[2]=>
array(1) {
["fruta"]=>
string(7) "limones"
}
}

Desordenaciones de matrices en PHP

Sabemos que existen muchos modos de ordenar una matriz, tanto unidimensional como multidimensional. Lo que no todo el mundo sabe es que PHP ofrece la posibilidad, necesaria en muchos casos, de alterar el orden de los elementos de una matriz, de forma que queden completamente desordenados. Para ello empleamos la función shuffle(), que recibe, como argumento, el nombre de la matriz a desordenar. Observe el siguiente fragmento de script:

$matriz = array("A", "B", "C", "D");
shuffle($matriz);
var_dump($matriz);

El resultado se parecerá al siguiente:

array(4)
[0]=>
string(1) "C"
[1]=>
string(1) "A"
[2]=>
string(1) "D"
[3]=>
string(1) "B"
}

Digo «se parecerá» porque los elementos son desordenados de modo aleatorio, lo que implica que, cada vez que se ejecute el script, se obtendrá un resultado diferente. Este sistema representa una limitación: no se mantine la paridad clave-valor de los elementos. En una matriz enumerativa puede que esto no tenga demasiada importancia, dependiendo del diseño del script y del uso que, en el mismo, se haga de dicha matriz. Pero créame, si su matriz es asociativa el desastre puede ser muy significativo. Observe lo siguiente:

$matriz = array("A"=>"Verde", "B"=>"Rojo", "C"=>"Amarillo", "D"=>"Azul");
shuffle($matriz);
var_dump($matriz);

Vea un resultado al ejecutar el fragmento anterior:

array(4) {
[0]=>
strin(5) "Verde"
[1]=>
string(8) "Amarillo"
[2]=>
string(4) "Azul"
[3]=>
string(4) "Rojo"
}

Como ve, no sólo se han perdido la paridades clave-valor sino que, en realidad, se han perdido hasta las propias claves.

Si queremos desordenar una matriz, manteniendo las claves y las correspondientes paridades, la solución pasa por una nueva función, llamada array_keys(). Esta genera una nueva matriz, a partir de la original, en la que las claves de la misma pasan a constituir los valores de los elementos. Esta nueva matriz es enumerativa. Observe el siguiente fragmento:

$matriz = array("A"=>"Verde", "B"=>"Rojo", "C"=>"Amarillo", "D"=>"Azul");
$claves =array_keys($matriz);
var_dump($claves);

El resultado será el siguiente:

array(4) {
[0]=>
string(1) "A"
[1]=>
string(1) "B"
[2]=>
string(1) "C"
[3]=>
string(1) "D"
}

Como ve, la matriz enumerativa $claves contiene tantos elementos como $matriz. Las claves de los elementos de $matriz constituyen ahora los valores de los elementos de $claves.

El siguiente paso es desordenar esta última, así:

$matriz = array("A"=>"Verde", "B"=>"Rojo", "C"=>"Amarillo", "D"=>"Azul");
$claves = array_keys($matriz);
shuffle($claves);
var_dump($claves);

Esto nos dará, por ejemplo, el siguiente resultado:

array(4) {
[0]=>
string(1) "D"
[1]=>
string(1) "B"
[2]=>
string(1) "A"
[3]=>
string(1) "C"
}

A continuación, creamos una matriz nueva en la que las claves de los elementos serán los valores de $claves, n¿una vez desordenados. Los valores de los elementos de la nueva matriz se obtendrán a partir de los valores que aparecen, originalmente, en $matriz, así:

$matriz = array("A"=>"Verde", "B"=>"Rojo", "C"=>"Amarillo", "D"=>"Azul");
$claves = array_keys($matriz);
shuffle($claves);
foreach($claves as $contenido){
$nuevaMatriz[$contenido] = $matriz[$contenido];
}
var_dump($nuevaMatriz);

El resultado será similar al siguiente:

array(4) {
["D"]=>
string(4) "Azul"
["B"]=>
string(4) "Rojo"
["A"]=>
string(5) "Verde"
["C"]=>
string(8) "Amarillo"
}

Como ve, los elementos de $matriz aparecen desordenados de modo aleatorio en $nuevaMatriz, pero manteniendo las paridades clave-valor originales, que es de lo que se trataba. Por supuesto, esta técnica puede emplearse también en matrices enumerativas o mixtas, si es necesario.

Y, puestos a hacer arbitrariedades con una matriz, puede ser que necesitemos extraer cierta cantidad aleatoria de claves, en lugar de recuperarlas todas, como hacemos con array_keys(). Para esto último, unaremos array_rand(). Esta función permite extraer una o más claves al azar de la matriz indicada. Recibe dos argumentos: el primero es el nombre de la matriz de la que se extraerán las claves y el segundo es el número de claves a extraer. Suponga el siguiente ejemplo:

$matriz = array("A"=>"Verde", "B"=>"Rojo", "C"=>"Amarillo", "D"=>"Azul");
$calves = array_rand($matriz, 2);
var_dump($claves);

El resultado podría ser el siguiente:

array(2) {
[0]=>
string(1) "D"
[1]=>
string(1) "C"
}

Tenga en cuenta que array_rand() no elimina las claves extraídas de la matriz original. Si necesita asegurarse de que, en posteriores llamadas a esta función, no se extraigan elementos repetidos, deberá crear un código que las elimine específicamente.

Comparte