Pregunta

Esta pregunta ya tiene respuesta aquí:

Tengo datos CSV cargados en una matriz multidimensional.De esta forma cada "fila" es un registro y cada "columna" contiene el mismo tipo de datos.Estoy usando la siguiente función 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 una columna para ordenar de modo 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 en la primera fila.

¿Fue útil?

Solución

Puedes usar matriz_multisort()

Pruebe 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 ordenar.No es necesario el bucle:

array_multisort(array_column($mdarray, 0), SORT_DESC, $mdarray);

Otros consejos

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

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

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

  1. Es reutilizable:usted especifica la columna de clasificación como una variable en lugar de codificarla.
  2. Es flexible:puede especificar varias columnas de clasificación (tantas como desee): se utilizan columnas adicionales como desempate entre elementos que inicialmente se comparan iguales.
  3. Es reversible:puede especificar que la clasificación se invierta, individualmente para cada columna.
  4. Es extensible:si el conjunto de datos contiene columnas que no se pueden comparar de manera "tonta" (p. ej.cadenas de fecha) también puede especificar cómo convertir estos elementos a un valor que pueda compararse directamente (p. ej.a DateTime instancia).
  5. Es asociativo si quieres:este código se encarga de clasificar los elementos, pero seleccione la función de clasificación real (usort o uasort).
  6. Finalmente, no utiliza array_multisort:mientras array_multisort es conveniente, depende de crear una proyección de todos sus datos de entrada antes de ordenarlos.Esto consume tiempo y memoria y puede resultar 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 utilizar

A lo largo de esta sección proporcionaré enlaces que clasifican 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'),
);

Los basicos

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

El caso de uso más sencillo es pasar la clave que desea utilizar para comparar elementos de datos.Por ejemplo, para ordenar $data por el name artículo que harías

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

Véalo en acción.

La clave también puede ser un número si los elementos son matrices indexadas numéricamente.Para el ejemplo de la pregunta, esto sería

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

Véalo en acción.

Varias columnas de clasificación

Puede especificar varias columnas de clasificació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

Hay funciones más avanzadas disponibles si especifica una columna de clasificación como una matriz en lugar de una cadena simple.Esta matriz debe estar 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 utilizar estas funciones.

Clasificación inversa

Para ordenar por nombre de forma 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 una columna cuyos valores no se prestan bien para ordenar.La columna "cumpleaños" del conjunto de datos de muestra se ajusta a esta descripción:No tiene sentido comparar los cumpleaños como cadenas (porque, p.e."01/01/1980" viene antes de "10/10/1970").En este caso queremos especificar cómo proyecto los datos reales a un formulario que poder compararse directamente con la semántica deseada.

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

Cabe señalar que si bien las proyecciones son similares a las funciones de comparación personalizadas utilizadas con usort y familia, son más simples (solo necesita convertir un valor en otro) y aprovechan todas las funciones ya integradas make_comparer.

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

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

Véalo en acción.

Ese no era 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 distinga entre mayúsculas y minúsculas es utilizar strtolower como proyección.

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

Finalmente, aquí hay un ejemplo que utiliza todas las funciones:primero ordena por número descendente, luego por fecha de nacimiento ascendente:

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

Véalo en acción.

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 la primera columna:

$sorter = new TableSorter(0); // sort by first column
$mdarray = $sorter->sort($mdarray);

Clasificación de varias filas mediante un cierre

Aquí hay otro enfoque que utiliza uasort() y una función de devolución de llamada anónima (cierre).He usado esa función regularmente. Se requiere PHP 5.3 – ¡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' ) );

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, nombre de columna) para ordenar.Se requiere PHP 5.3.

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

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/

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

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'));

Antes de poder ejecutar la clase TableSorter, se me ocurrió una función basada en lo que Shin Han 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 es el MD Array que desea ordenar.
  • $column es la columna por la que desea ordenar.
  • $method es cómo desea que se realice la clasificación, como SORT_DESC
  • $has_header se establece en verdadero si la primera fila contiene valores de encabezado que no desea ordenar.

Probé varias respuestas populares de array_multisort() y usort() y ninguna funcionó para mí.Los datos simplemente se confunden y el código es ilegible.Aquí tienes una solución rápida y sucia.ADVERTENCIA:¡Úsalo sólo si estás seguro de que un delimitador malicioso no volverá a perseguirte más tarde!

Digamos que cada fila de su matriz múltiple se ve así:nombre, cosas1, cosas2:

// 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);

¿Necesitas que te devuelvan tus cosas 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 es súper fácil y no te explotará la cabeza.

Prefiero usar array_multisort.Ver la documentaciónaquí.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top