Tutoriales

Cómo ordenar un array de claves en PHP basándonos en otro array secundario que contenga el orden

Cuando estáis desarrollando en PHP, como nosotros con WordPress o WooCommerce, en más de una ocasión por ejemplo cuando haces una llamada a un filtro en WordPress y devuelves un array, te gustaría hacerlo ordenado de otra forma. Cosas que se me ocurren: elemento de «Mi cuenta» en WooCommerce, métodos de envío en WooCommerce o tantos otros. El caso es que muchos de estos array están referenciados a una cadena, lo que también llamamos matriz asociativa, array asociativo o tabla hash; y no es tan inmediato reordenarlos.

Pues bueno como es un problema con el que me enfrento a menudo, voy a describir unas pocas soluciones.

Os doy primero el array con los datos y el array que marcará el orden.

$datos_usuario = array(
  'first_name' => 'Javier', 
  'user_email' => 'carazo@codection.com', 
  'last_login' => '2020-01-25', 
  'last_name' => 'Carazo'
);

$orden = array('first_name', 'last_name', 'user_email', 'last_login');

El resultado que deseamos es el siguiente por lo tanto:

$datos_usuario = array( 'first_name' => 'Javier', 'last_name' => 'Carazo', 'user_email' => 'carazo@codection.com', 'last_login' => '2020-01-25' );

Veamos tres formas diferentes de hacerlo.

Usando array_merge()

$array_ordenado = array_merge( array_flip( $orden ), $datos_usuario );
  • array_flip(): convierte valores en claves y viceversa, como en este caso solo tiene valores, los convierte en claves
  • array_merge(): como los dos arrays tienen las mismas claves, los valores del segundo array son escritos en el orden del primero

Usando array_replace()

$array_ordenado = array_replace( array_flip( $orden ), $datos_usuario );
  • array_flip(): hace lo mismo que antes, convertir un array de valores en un array de claves
  • array_replace(): reemplaza los valores del primer arary con los valores del segundo teniendo en cuenta el orden del primero, al ir sustituyendo sus términos, vacíos, por los del segundo, rellenos

Usando uksort()

uksort( $datos_usuario, function( $key1, $key2 ) use ( $orden ) {
  return ( ( array_search( $key1, $orden ) > array_search( $key2, $orden ) ) ? 1 : -1 );
});
  • uksort() permite ordenar un array por claves usando una función de ordenación, que se llama en cada comparación de dos elementos
  • La función que se ha definido, hace búsquedas sobre el array de orden de forma que el array $datos_usuario se ordena en base al orden del array de orden

Por gustarme más me gusta más uno de los dos primeros métodos (por su limpieza de código), aunque si en los comentarios podéis aportar vuestros pensamientos de cuál de ellas es más rápida, será interesante saberlo para cuando el array a ordenar sea de cierto tamaño. Y si conocéis otro método más, encantado de que lo aportéis.

Actualizado

Gracias a la aportación de Manuel Canga, en este tweet:

Incluimos otro método más basado en el uso de foreach:

foreach( $order as $field ){
   $datos_usuario_ordenado[ $field ] = $datos_usuario[ $field ];
}

¿Cuál método es más rápido?

Seguimos actualizando este post y es que Manuel Canga ha tenido el detalle de calcular tiempos, para ver cuál es más rápido y aquí os dejo el resultado. Tenéis la ejecución online aquí en PHP Sandbox.

Array merge: 1580140177.172296
Array replace: 1580140177.453599
Array uksort: 1580140178.319940
Foreach: 1580140178.606628

<?php

const MAX_LOOPS = 1000000; // 1 million 

$datos_usuario = array(
  'first_name' => 'Javier', 
  'user_email' => 'carazo@codection.com', 
  'last_login' => '2020-01-25', 
  'last_name' => 'Carazo'
);
$orden = array('first_name', 'last_name', 'user_email', 'last_login');
    
    

$time_start = microtime(true); 

for($i = 0; $i<MAX_LOOPS; $i++ ) {
    $array_ordenado = array_merge( array_flip( $orden ), $datos_usuario );
}

$time_end = microtime(true);

printf("\nArray merge: %f", $time_end);


//-------

$time_start = microtime(true); 

for($i = 0; $i<MAX_LOOPS; $i++ ) {
    $array_ordenado = array_replace( array_flip( $orden ), $datos_usuario );
}

$time_end = microtime(true);

printf("\nArray replace: %f", $time_end);

//-------

$time_start = microtime(true); 

for($i = 0; $i<MAX_LOOPS; $i++ ) {
    uksort( $datos_usuario, function( $key1, $key2 ) use ( $orden ) {
      return ( ( array_search( $key1, $orden ) > array_search( $key2, $orden ) ) ? 1 : -1 );
    });
}

$time_end = microtime(true);

printf("\nArray uksort: %f", $time_end);

//-------

$time_start = microtime(true); 

for($i = 0; $i<MAX_LOOPS; $i++ ) {
    $array_ordenado = []; 
    foreach($orden as $field ) {
        $array_ordenado[$field] = $datos_usuario[$field];
    }
}

$time_end = microtime(true);

printf("\nForeach: %f", $time_end);

Se han hecho un total de 1 millón de iteraciones sobre cada operación para así ver el rendimiento de cada instrucción, sobre esta estructura de datos. Otra prueba diferente sería ordenar una estructura de datos grande (que no tienen por qué ser igual los resultados) y de estos resultados podemos deducir que en estructuras pequeñas como esta:

  • Los tiempos son prácticamente iguales
  • En todo caso, los primeros métodos son algo más ligeros que los últimos

Y por lo tanto, os invito a hacerlo como más os guste. A mí personalmente me gustan más los primeros también por su sencillez a la hora de escribirlos, así que me quedo con ellos por dos motivos, sencillo y rápido.

Si tenéis más aportaciones, ya sabéis a través de tweets o comentarios podemos agregarlas.Nota: Hay una valoración incrustada en esta entrada, por favor, visita esta entrada para valorarla.

Almacenamos las IPs desde la que se envían las valoraciones para evitar fraudes

Francisco Javier Carazo Gil

Entradas recientes

¿Por qué falla Bizum en WooCommerce? 9 causas

Por qué falla Bizum WooCommerce: detecta errores de credenciales, firma, entorno y pedidos para recuperar…

hace % días

Plugin gratuito Redsys WordPress: qué revisar

Elige un plugin gratuito Redsys WordPress con criterio: compatibilidad, seguridad, configuración bancaria y límites para…

hace % días

Qué necesito para activar CECA Online en mi web

¿Qué necesito para activar CECA Online? Revisa contrato bancario, datos del terminal, seguridad, pruebas y…

hace % días

Cómo elegir una pasarela para suscripciones

Aprende a elegir una pasarela para suscripciones en WordPress: revisa recurrencia, compatibilidad, seguridad y soporte…

hace % días

Plugin Bizum WordPress para cobrar con control

Elige un plugin Bizum WordPress compatible con tu flujo de venta, configura la pasarela bancaria…

hace % días

Cómo aceptar CECA en reservas de WordPress

Aprende cómo aceptar CECA en reservas WordPress, configurar pagos seguros y confirmar cada reserva sin…

hace % días