Cómo ordenar un Array Multidimensional en PHP [duplicar]


Esta pregunta ya tiene una respuesta aquí:

Tengo datos CSV cargados en una matriz multidimensional. De esta manera, cada fila es un registro y cada "columna" contiene el mismo tipo de datos. Estoy usando la función de abajo para cargar mi archivo CSV.

function f_parse_csv($file, $longest, $delimiter)
{
  $mdarray = array();
  $file    = fopen($file, "r");
  while ($line = fgetcsv($file, $longest, $delimiter))
  {
    array_push($mdarray, $line);
  }
  fclose($file);
  return $mdarray;
}

Necesito poder especificar un columna a ordenar para que reorganice las filas. Una de las columnas contiene información de fecha en el formato de Y-m-d H:i:s y me gustaría poder ordenar con la fecha más reciente siendo la primera fila.

Author: hakre, 2008-09-19

11 answers

Puedes usar array_multisort()

Intenta algo como esto:

foreach ($mdarray as $key => $row) {
    // replace 0 with the field's index/key
    $dates[$key]  = $row[0];
}

array_multisort($dates, SORT_DESC, $mdarray);

Para PHP >= 5.5.0 simplemente extraiga la columna para ordenarla. No es necesario el bucle:

array_multisort(array_column($mdarray, 0), SORT_DESC, $mdarray);
 202
Author: Shinhan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-08 17:18:23

Introducción: una solución muy generalizada para PHP 5.3+

Me gustaría añadir mi propia solución aquí, ya que ofrece características que otras respuestas no.

Específicamente, las ventajas de esta solución incluyen:

  1. Es reutilizable: se especifica la columna de ordenación como una variable en lugar de codificarla.
  2. Es flexible : puede especificar varias columnas de ordenación (tantas como desee) columns se utilizan columnas adicionales como desempate entre elementos que inicialmente se comparan iguales.
  3. Es reversible: puede especificar que el tipo debe ser revertido-individual para cada columna.
  4. Es extensible: si el conjunto de datos contiene columnas que no se pueden comparar de una manera "tonta" (por ejemplo, cadenas de fecha) también puede especificar cómo convertir estos elementos a un valor que se puede comparar directamente (por ejemplo, una instancia DateTime).
  5. Es asociativo si quieres : este código se encarga de ordenar items, but you select the actual sort function (usort or uasort).
  6. Finalmente, no usa array_multisort: mientras que array_multisort es conveniente, depende de crear una proyección de todos los datos de entrada antes de ordenar. Esto consume tiempo y memoria y puede ser simplemente prohibitivo si su conjunto de datos es grande.

El código

function make_comparer() {
    // Normalize criteria up front so that the comparer finds everything tidy
    $criteria = func_get_args();
    foreach ($criteria as $index => $criterion) {
        $criteria[$index] = is_array($criterion)
            ? array_pad($criterion, 3, null)
            : array($criterion, SORT_ASC, null);
    }

    return function($first, $second) use (&$criteria) {
        foreach ($criteria as $criterion) {
            // How will we compare this round?
            list($column, $sortOrder, $projection) = $criterion;
            $sortOrder = $sortOrder === SORT_DESC ? -1 : 1;

            // If a projection was defined project the values now
            if ($projection) {
                $lhs = call_user_func($projection, $first[$column]);
                $rhs = call_user_func($projection, $second[$column]);
            }
            else {
                $lhs = $first[$column];
                $rhs = $second[$column];
            }

            // Do the actual comparison; do not return if equal
            if ($lhs < $rhs) {
                return -1 * $sortOrder;
            }
            else if ($lhs > $rhs) {
                return 1 * $sortOrder;
            }
        }

        return 0; // tiebreakers exhausted, so $first == $second
    };
}

Cómo usar

A lo largo de esta sección proporcionaré enlaces que ordenan este conjunto de datos de muestra:

$data = array(
    array('zz', 'name' => 'Jack', 'number' => 22, 'birthday' => '12/03/1980'),
    array('xx', 'name' => 'Adam', 'number' => 16, 'birthday' => '01/12/1979'),
    array('aa', 'name' => 'Paul', 'number' => 16, 'birthday' => '03/11/1987'),
    array('cc', 'name' => 'Helen', 'number' => 44, 'birthday' => '24/06/1967'),
);

El lo basico

La función make_comparer acepta un número variable de argumentos que definen el orden deseado y devuelve una función que se supone que debe usar como argumento para usort o uasort.

El caso de uso más simple es pasar la clave que le gustaría usar para comparar elementos de datos. Por ejemplo, para ordenar $data por el elemento name haría

usort($data, make_comparer('name'));

Véalo en acción.

La clave también puede ser un número si los elementos están indexados numéricamente matriz. Para el ejemplo en la pregunta, esto sería

usort($data, make_comparer(0)); // 0 = first numerically indexed column

Véalo en acción.

Múltiples columnas de ordenación

Puede especificar varias columnas de ordenación pasando parámetros adicionales a make_comparer. Por ejemplo, para ordenar por "número" y luego por la columna indexada a cero:

usort($data, make_comparer('number', 0));

Véalo en acción.

Características avanzadas

Las características más avanzadas están disponibles si especifica una columna de ordenación como una matriz en su lugar de una simple cuerda. Esta matriz debe ser indexada numéricamente, y debe contener estos elementos:

0 => the column name to sort on (mandatory)
1 => either SORT_ASC or SORT_DESC (optional)
2 => a projection function (optional)

Veamos cómo podemos usar estas características.

Ordenación inversa

Para ordenar por nombre descendente:

usort($data, make_comparer(['name', SORT_DESC]));

Véalo en acción.

Para ordenar por número descendente y luego por nombre descendente:

usort($data, make_comparer(['number', SORT_DESC], ['name', SORT_DESC]));

Véalo en acción.

Proyecciones personalizadas

En algunos escenarios es posible que necesite ordenar por un columna cuyos valores no se prestan bien a la ordenación. La columna " cumpleaños "en el conjunto de datos de muestra se ajusta a esta descripción: no tiene sentido comparar cumpleaños como cadenas (porque, por ejemplo," 01/01/1980 "viene antes de"10/10/1970"). En este caso queremos especificar cómo proyectar los datos reales a un formulario que puede compararse directamente con la semántica deseada.

Las proyecciones se pueden especificar como cualquier tipo de llamable : como cadenas, matrices o anónimas función. Se supone que una proyección acepta un argumento y devuelve su forma proyectada.

Debe tenerse en cuenta que si bien las proyecciones son similares a las funciones de comparación personalizadas utilizadas con usort y family, son más simples (solo necesita convertir un valor a otro) y aprovechar toda la funcionalidad ya horneada en make_comparer.

Ordenemos el conjunto de datos de ejemplo sin una proyección y veamos qué sucede:

usort($data, make_comparer('birthday'));

Ver en acción.

Ese no fue el resultado deseado. Pero podemos usar date_create como proyección:

usort($data, make_comparer(['birthday', SORT_ASC, 'date_create']));

Véalo en acción.

Este es el orden correcto que queríamos.

Hay muchas más cosas que las proyecciones pueden lograr. Por ejemplo, una forma rápida de obtener una clasificación que no distingue entre mayúsculas y minúsculas es usar strtolower como proyección.

Dicho esto, también debo mencionar que es mejor no usar proyecciones si su conjunto de datos es grande: en ese caso, sería mucho más rápido proyectar todos sus datos manualmente por adelantado y luego ordenar sin usar una proyección, aunque al hacerlo, se intercambiará un mayor uso de memoria por una velocidad de clasificación más rápida.

Finalmente, aquí hay un ejemplo que usa todas las características: primero ordena por número descendente, luego por cumpleaños ascendente:

usort($data, make_comparer(
    ['number', SORT_DESC],
    ['birthday', SORT_ASC, 'date_create']
));

Véalo en acción.

 332
Author: Jon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-28 14:34:33

Con usort . Aquí hay una solución genérica, que puede usar para diferentes columnas:

class TableSorter {
  protected $column;
  function __construct($column) {
    $this->column = $column;
  }
  function sort($table) {
    usort($table, array($this, 'compare'));
    return $table;
  }
  function compare($a, $b) {
    if ($a[$this->column] == $b[$this->column]) {
      return 0;
    }
    return ($a[$this->column] < $b[$this->column]) ? -1 : 1;
  }
}

Para ordenar por primera columna:

$sorter = new TableSorter(0); // sort by first column
$mdarray = $sorter->sort($mdarray);
 31
Author: troelskn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-18 20:53:11

Ordenación de filas múltiples mediante un cierre

Aquí hay otro enfoque usando uasort() y una función de devolución de llamada anónima (closure). He usado esa función regularmente. PHP 5.3 requerido - ¡no más dependencias!

/**
 * Sorting array of associative arrays - multiple row sorting using a closure.
 * See also: http://the-art-of-web.com/php/sortarray/
 *
 * @param array $data input-array
 * @param string|array $fields array-keys
 * @license Public Domain
 * @return array
 */
function sortArray( $data, $field ) {
    $field = (array) $field;
    uasort( $data, function($a, $b) use($field) {
        $retval = 0;
        foreach( $field as $fieldname ) {
            if( $retval == 0 ) $retval = strnatcmp( $a[$fieldname], $b[$fieldname] );
        }
        return $retval;
    } );
    return $data;
}

/* example */
$data = array(
    array( "firstname" => "Mary", "lastname" => "Johnson", "age" => 25 ),
    array( "firstname" => "Amanda", "lastname" => "Miller", "age" => 18 ),
    array( "firstname" => "James", "lastname" => "Brown", "age" => 31 ),
    array( "firstname" => "Patricia", "lastname" => "Williams", "age" => 7 ),
    array( "firstname" => "Michael", "lastname" => "Davis", "age" => 43 ),
    array( "firstname" => "Sarah", "lastname" => "Miller", "age" => 24 ),
    array( "firstname" => "Patrick", "lastname" => "Miller", "age" => 27 )
);

$data = sortArray( $data, 'age' );
$data = sortArray( $data, array( 'lastname', 'firstname' ) );
 10
Author: feeela,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-13 14:38:54

Sé que han pasado 2 años desde que se hizo y respondió esta pregunta, pero aquí hay otra función que ordena una matriz bidimensional. Acepta un número variable de argumentos, lo que le permite pasar más de una clave (es decir, el nombre de la columna) para ordenar por. PHP 5.3 requerido.

function sort_multi_array ($array, $key)
{
  $keys = array();
  for ($i=1;$i<func_num_args();$i++) {
    $keys[$i-1] = func_get_arg($i);
  }

  // create a custom search function to pass to usort
  $func = function ($a, $b) use ($keys) {
    for ($i=0;$i<count($keys);$i++) {
      if ($a[$keys[$i]] != $b[$keys[$i]]) {
        return ($a[$keys[$i]] < $b[$keys[$i]]) ? -1 : 1;
      }
    }
    return 0;
  };

  usort($array, $func);

  return $array;
}

Pruébalo aquí: http://www.exorithm.com/algorithm/view/sort_multi_array

 6
Author: Mike C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-19 23:21:51
function cmp($a, $b)
{
$p1 = $a['price'];
$p2 = $b['price'];
return (float)$p1 > (float)$p2;
}
uasort($my_array, "cmp");

Http://qaify.com/sort-an-array-of-associative-arrays-by-value-of-given-key-in-php /

 6
Author: Kamal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-24 07:51:05

La función "Usort" es tu respuesta.
http://php.net/usort

 3
Author: Jan Hančič,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-13 18:43:22

Aquí hay una clase php4 / php5 que ordenará uno o más campos:

// a sorter class
//  php4 and php5 compatible
class Sorter {

  var $sort_fields;
  var $backwards = false;
  var $numeric = false;

  function sort() {
    $args = func_get_args();
    $array = $args[0];
    if (!$array) return array();
    $this->sort_fields = array_slice($args, 1);
    if (!$this->sort_fields) return $array();

    if ($this->numeric) {
      usort($array, array($this, 'numericCompare'));
    } else {
      usort($array, array($this, 'stringCompare'));
    }
    return $array;
  }

  function numericCompare($a, $b) {
    foreach($this->sort_fields as $sort_field) {
      if ($a[$sort_field] == $b[$sort_field]) {
        continue;
      }
      return ($a[$sort_field] < $b[$sort_field]) ? ($this->backwards ? 1 : -1) : ($this->backwards ? -1 : 1);
    }
    return 0;
  }

  function stringCompare($a, $b) {
    foreach($this->sort_fields as $sort_field) {
      $cmp_result = strcasecmp($a[$sort_field], $b[$sort_field]);
      if ($cmp_result == 0) continue;

      return ($this->backwards ? -$cmp_result : $cmp_result);
    }
    return 0;
  }
}

/////////////////////
// usage examples

// some starting data
$start_data = array(
  array('first_name' => 'John', 'last_name' => 'Smith', 'age' => 10),
  array('first_name' => 'Joe', 'last_name' => 'Smith', 'age' => 11),
  array('first_name' => 'Jake', 'last_name' => 'Xample', 'age' => 9),
);

// sort by last_name, then first_name
$sorter = new Sorter();
print_r($sorter->sort($start_data, 'last_name', 'first_name'));

// sort by first_name, then last_name
$sorter = new Sorter();
print_r($sorter->sort($start_data, 'first_name', 'last_name'));

// sort by last_name, then first_name (backwards)
$sorter = new Sorter();
$sorter->backwards = true;
print_r($sorter->sort($start_data, 'last_name', 'first_name'));

// sort numerically by age
$sorter = new Sorter();
$sorter->numeric = true;
print_r($sorter->sort($start_data, 'age'));
 2
Author: Devon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-18 22:16:00

Antes de que pudiera ejecutar la clase Tablorter, se me ocurrió una función basada en lo que Shinhan había proporcionado.

function sort2d_bycolumn($array, $column, $method, $has_header)
  {
  if ($has_header)  $header = array_shift($array);
  foreach ($array as $key => $row) {
    $narray[$key]  = $row[$column]; 
    }
  array_multisort($narray, $method, $array);
  if ($has_header) array_unshift($array, $header);
  return $array;
  }
  • array array es la matriz MD que desea ordenar.
  • column column es la columna por la que desea ordenar.
  • method el método es cómo desea que se realice la ordenación, como SORT_DESC
  • has has_header se establece en true si la primera fila contiene valores de encabezado que no desea ordenar.
 0
Author: Melikoth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:03:04

Probé varias respuestas populares array_multisort() y usort() y ninguna de ellas funcionó para mí. Los datos se confunden y el código es ilegible. Aquí hay una solución rápida y sucia. ADVERTENCIA: ¡Solo usa esto si estás seguro de que un delimitador falso no volverá para atormentarte más tarde!

Digamos que cada fila en tu multi array se ve como: name, stuff1, stuff2:

// Sort by name, pull the other stuff along for the ride
foreach ($names_stuff as $name_stuff) {
    // To sort by stuff1, that would be first in the contatenation
    $sorted_names[] = $name_stuff[0] .','. name_stuff[1] .','. $name_stuff[2];
}
sort($sorted_names, SORT_STRING);

¿Necesita que sus cosas vuelvan en orden alfabético?

foreach ($sorted_names as $sorted_name) {
    $name_stuff = explode(',',$sorted_name);
    // use your $name_stuff[0] 
    // use your $name_stuff[1] 
    // ... 
}

Sí, está sucio. Pero super fácil, no hará tu cabeza explota.

 0
Author: PJ Brunet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-19 21:00:25

Prefiero usar array_multisort. Ver la documentación aquí.

 -1
Author: Tim Boland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-03 03:59:30