Come posso ordinare un array multidimensionale in PHP [duplicato]
-
01-07-2019 - |
Domanda
Questa domanda ha già una risposta qui:
- Come posso ordinare array e dati in PHP? 10 risposte
Ho caricato i dati CSV in un array multidimensionale.In questo modo ogni "riga" è un record e ogni "colonna" contiene lo stesso tipo di dati.Sto utilizzando la funzione seguente per caricare il mio file 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;
}
Devo essere in grado di specificare una colonna da ordinare in modo da riorganizzare le righe.Una delle colonne contiene informazioni sulla data nel formato di Y-m-d H:i:s
e vorrei poter ordinare con la data più recente nella prima riga.
Soluzione
Puoi usare array_multisort()
Prova qualcosa del genere:
foreach ($mdarray as $key => $row) {
// replace 0 with the field's index/key
$dates[$key] = $row[0];
}
array_multisort($dates, SORT_DESC, $mdarray);
Per PHP >= 5.5.0 basta estrarre la colonna in base alla quale ordinare.Non è necessario il ciclo:
array_multisort(array_column($mdarray, 0), SORT_DESC, $mdarray);
Altri suggerimenti
Presentazione:una soluzione molto generalizzata per PHP 5.3+
Vorrei aggiungere qui la mia soluzione, poiché offre funzionalità che altre risposte non offrono.
Nello specifico i vantaggi di questa soluzione includono:
- Suo riutilizzabile:specifichi la colonna di ordinamento come variabile invece di codificarla.
- Suo flessibile:puoi specificare più colonne di ordinamento (quante ne vuoi): le colonne aggiuntive vengono utilizzate come separatori tra gli elementi che inizialmente risultano uguali.
- Suo reversibile:puoi specificare che l'ordinamento deve essere invertito, individualmente per ciascuna colonna.
- Suo estensibile:se il set di dati contiene colonne che non possono essere confrontate in modo "stupido" (ad es.stringhe di data) puoi anche specificare come convertire questi elementi in un valore che possa essere direttamente confrontato (es.UN
DateTime
esempio). - Suo associativo se vuoi:questo codice si occupa dell'ordinamento degli elementi, ma Voi selezionare la funzione di ordinamento effettiva (
usort
Ouasort
). - Infine, non utilizza
array_multisort
:Mentrearray_multisort
è conveniente, dipende dalla creazione di una proiezione di tutti i dati di input prima dell'ordinamento.Ciò consuma tempo e memoria e potrebbe essere semplicemente proibitivo se il set di dati è di grandi dimensioni.
Il codice
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
};
}
Come usare
In questa sezione fornirò i collegamenti che ordinano questo set di dati di esempio:
$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'),
);
Le basi
La funzione make_comparer
accetta un numero variabile di argomenti che definiscono l'ordinamento desiderato e restituisce una funzione che dovresti utilizzare come argomento usort
O uasort
.
Il caso d'uso più semplice è passare la chiave che desideri utilizzare per confrontare gli elementi di dati.Ad esempio, per ordinare $data
dal name
oggetto che faresti
usort($data, make_comparer('name'));
La chiave può anche essere un numero se gli elementi sono matrici indicizzate numericamente.Per l'esempio nella domanda, questo sarebbe
usort($data, make_comparer(0)); // 0 = first numerically indexed column
Colonne di ordinamento multiple
È possibile specificare più colonne di ordinamento passando parametri aggiuntivi a make_comparer
.Ad esempio, per ordinare in base al "numero" e quindi alla colonna con indice zero:
usort($data, make_comparer('number', 0));
Funzionalità avanzate
Sono disponibili funzionalità più avanzate se si specifica una colonna di ordinamento come array anziché come semplice stringa.Questo array deve essere indicizzato numericamente e deve contenere questi elementi:
0 => the column name to sort on (mandatory)
1 => either SORT_ASC or SORT_DESC (optional)
2 => a projection function (optional)
Vediamo come possiamo utilizzare queste funzionalità.
Ordinamento inverso
Per ordinare per nome discendente:
usort($data, make_comparer(['name', SORT_DESC]));
Per ordinare per numero discendente e poi per nome discendente:
usort($data, make_comparer(['number', SORT_DESC], ['name', SORT_DESC]));
Proiezioni personalizzate
In alcuni scenari potrebbe essere necessario ordinare in base a una colonna i cui valori non si prestano bene all'ordinamento.La colonna "compleanno" nel set di dati di esempio corrisponde a questa descrizione:non ha senso confrontare i compleanni come stringhe (perché ad es."01/01/1980" viene prima di "10/10/1970").In questo caso vogliamo specificare come fare progetto i dati effettivi in un modulo che Potere essere confrontato direttamente con la semantica desiderata.
Le proiezioni possono essere specificate come qualsiasi tipo di richiamabile:come stringhe, array o funzioni anonime.Si presuppone che una proiezione accetti un argomento e restituisca la sua forma proiettata.
Va notato che mentre le proiezioni sono simili alle funzioni di confronto personalizzate utilizzate con usort
e famiglia, sono più semplici (è sufficiente convertire un valore in un altro) e sfruttano tutte le funzionalità già integrate make_comparer
.
Ordiniamo il set di dati di esempio senza proiezione e vediamo cosa succede:
usort($data, make_comparer('birthday'));
Questo non era il risultato desiderato.Ma possiamo usare date_create
come proiezione:
usort($data, make_comparer(['birthday', SORT_ASC, 'date_create']));
Questo è l'ordine corretto che volevamo.
Ci sono molte altre cose che le proiezioni possono ottenere.Ad esempio, un modo rapido per ottenere un ordinamento senza distinzione tra maiuscole e minuscole è utilizzare strtolower
come proiezione.
Detto questo, dovrei anche menzionare che è meglio non utilizzare le proiezioni se il set di dati è di grandi dimensioni:in tal caso sarebbe molto più veloce proiettare manualmente tutti i dati in anticipo e quindi ordinarli senza utilizzare una proiezione, anche se così facendo si scambierebbe un maggiore utilizzo della memoria con una maggiore velocità di ordinamento.
Infine, ecco un esempio che utilizza tutte le funzionalità:prima ordina per numero discendente, poi per compleanno ascendente:
usort($data, make_comparer(
['number', SORT_DESC],
['birthday', SORT_ASC, 'date_create']
));
Con usort.Ecco una soluzione generica, che puoi utilizzare per diverse colonne:
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;
}
}
Per ordinare in base alla prima colonna:
$sorter = new TableSorter(0); // sort by first column
$mdarray = $sorter->sort($mdarray);
Ordinamento di più righe utilizzando una chiusura
Ecco un altro approccio che utilizza uasort() e una funzione di callback anonima (chiusura).Ho usato quella funzione regolarmente. È richiesto PHP 5.3 – niente più dipendenze!
/**
* 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' ) );
So che sono passati 2 anni da quando è stata posta e data risposta a questa domanda, ma ecco un'altra funzione che ordina un array bidimensionale.Accetta un numero variabile di argomenti, consentendoti di passare più di una chiave (ad esempio il nome della colonna) in base a cui ordinare.È richiesto 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;
}
Provalo qui: 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 funzione "Usort" è la tua risposta.
http://php.net/usort
Ecco una classe php4/php5 che ordinerà uno o più campi:
// 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'));
Prima di poter eseguire la classe TableSorter, avevo ideato una funzione basata su what Shinhan aveva fornito.
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 è l'array MD che desideri ordinare.
- $colonna è la colonna in base alla quale desideri ordinare.
- $method è il modo in cui desideri che venga eseguito l'ordinamento, ad esempio SORT_DESC
- $has_header è impostato su true se la prima riga contiene valori di intestazione che non desideri ordinare.
Ho provato diverse risposte popolari array_multisort() e usort() e nessuna ha funzionato per me.I dati vengono semplicemente confusi e il codice è illeggibile.Ecco una soluzione rapida e sporca.AVVERTIMENTO:Usalo solo se sei sicuro che un delimitatore canaglia non tornerà a perseguitarti più tardi!
Diciamo che ogni riga nel tuo multi array assomiglia a:nome, roba1, roba2:
// 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);
Hai bisogno di riportare le tue cose in ordine alfabetico?
foreach ($sorted_names as $sorted_name) {
$name_stuff = explode(',',$sorted_name);
// use your $name_stuff[0]
// use your $name_stuff[1]
// ...
}
Sì, è sporco.Ma super facile, non ti farà esplodere la testa.
Preferisco usare array_multisort.Consulta la documentazioneQui.