Comment trier un tableau multidimensionnel en PHP [dupliquer]

cette question a déjà une réponse ici:

  • Comment puis-je trier les tableaux et les données en PHP? 9 réponses

J'ai des données CSV chargées dans un tableau multidimensionnel. De cette façon, chaque "ligne" est un enregistrement et chaque "colonne" contient le même type de données. Je suis l'aide de la fonction ci-dessous pour charger mon fichier 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;
}

j'ai besoin de pouvoir spécifier une colonne à trier pour qu'elle réarrange les lignes. Une des colonnes contient des informations de date dans le format Y-m-d H:i:s et je voudrais pouvoir trier avec la date la plus récente étant la première rangée.

191
demandé sur hakre 2008-09-19 00:45:56

11 réponses

vous pouvez utiliser array_multisort ()

Essayez quelque chose comme ceci:

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

array_multisort($dates, SORT_DESC, $mdarray);

pour PHP >= 5.5.0 il suffit d'extraire la colonne pour trier par. Pas besoin de boucle:

array_multisort(array_column($mdarray, 0), SORT_DESC, $mdarray);
202
répondu Shinhan 2017-08-08 17:18:23

introduction: une solution très généralisée pour PHP 5.3+

j'aimerais ajouter ma propre solution ici, car elle offre des fonctionnalités que d'autres réponses ne le font pas.

plus précisément, les avantages de cette solution comprennent:

  1. c'est réutilisable : vous spécifiez la colonne sort comme une variable au lieu de la codifier.
  2. c'est flexible : vous peut spécifier plusieurs colonnes de tri (autant que vous voulez) -- des colonnes supplémentaires sont utilisées comme briseurs de tie entre des éléments qui comparent initialement equal.
  3. c'est réversible : vous pouvez spécifier que le tri doit être inversé -- individuellement pour chaque colonne.
  4. C'est extensible : si l'ensemble de données contient des colonnes qui ne peuvent pas être comparées d'une manière" stupide " (par exemple chaînes de date), vous pouvez également spécifier comment convertissez ces éléments en une valeur qui peut être directement comparée (par exemple une instance DateTime ).
  5. C'est associatif si vous voulez : ce code s'occupe des articles de tri, mais vous sélectionnez la fonction de tri réelle ( usort ou uasort ).
  6. enfin, il n'utilise pas array_multisort : alors que array_multisort est pratique, il dépend de la création d'une projection de toutes vos données d'entrée avant tri. Cela consomme du temps et de la mémoire et peut être tout simplement prohibitif si votre ensemble de données est grand.

le code

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
    };
}

comment utiliser

tout au long de cette section, Je fournirai des liens qui trient cet ensemble de données d'échantillon:

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

Les bases

La fonction make_comparer accepte un nombre variable d'arguments qui définissent le tri désiré et renvoie une fonction que vous êtes censé utiliser comme argument pour usort ou uasort .

le cas d'utilisation le plus simple est de passer dans la clé que vous aimez utiliser pour comparer des éléments de données. Par exemple, pour trier $data par le name article que vous feriez

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

voir action .

La clé peut également être un nombre si les éléments sont tableaux indexés numériquement. Pour l'exemple de la question, ce serait

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

Voir en action .

colonnes de tri multiples

Vous pouvez spécifier plusieurs colonnes de tri en passant des paramètres supplémentaires à make_comparer . Par exemple, pour trier par "nombre" et ensuite par la colonne indexée à zéro:

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

Voir en action .

fonctionnalités Avancées

des fonctionnalités plus avancées sont disponibles si vous spécifiez une colonne de tri comme un tableau au lieu d'une chaîne simple. Ce tableau doit être indexé numériquement, et doit contenir les éléments suivants:

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

voyons comment nous pouvons utiliser ces fonctionnalités.

tri Inverse

pour trier par Nom descendant:

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

Voir en action .

pour trier par numéro descendant puis par Nom descendant:

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

voir action .

"1519580920 Personnalisé" projections

dans certains scénarios, vous pouvez avoir besoin de trier par une colonne dont les valeurs ne se prêtent pas bien au tri. Le la colonne " anniversaire "de l'ensemble de données de l'échantillon correspond à cette description: il n'est pas logique de comparer les anniversaires comme chaînes de caractères (parce que par exemple" 01/01/1980 "vient avant"10/10/1970"). Dans ce cas, nous voulons spécifier comment projet les données réelles à un formulaire que peut être comparé directement avec la sémantique désirée.

Projections peuvent être spécifiées comme n'importe quel type de appelable : comme chaînes, tableaux, ou les fonctions anonymes. Une projection est supposée accepter un argument et retourner sa forme projetée.

il est à noter que bien que les projections sont similaires aux fonctions de comparaison personnalisées utilisées avec usort et la famille, ils sont plus simples (il suffit de convertir une valeur à une autre) et de profiter de toutes les fonctionnalités déjà cuites dans make_comparer .

trions l'ensemble de données de l'exemple sans projection et voyons ce que se passe:

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

voir action .

ce n'était pas le résultat souhaité. Mais nous pouvons utiliser date_create comme une projection:

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

Voir en action .

C'est l'ordre correct que nous voulions.

il y a beaucoup d'autres choses que les projections peuvent accomplir. Par exemple, un moyen rapide d'obtenir un tri insensible à la casse est d'utiliser strtolower comme projection.

cela dit, je dois également mentionner qu'il est préférable de ne pas utiliser des projections si votre ensemble de données est grande: dans ce cas, il serait beaucoup plus rapide de projeter toutes vos données manuellement à l'avant et puis trier sans utiliser une projection, bien que ce faisant sera l'échange de l'utilisation de la mémoire accrue pour une vitesse de tri plus rapide.

enfin, voici un exemple qui utilise toutes les fonctionnalités: il trie d'abord par nombre descendant, puis par anniversaire Ascendant:

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

Voir en action .

332
répondu Jon 2013-06-28 14:34:33

avec usort . Voici une solution générique, que vous pouvez utiliser pour différentes colonnes:

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;
  }
}

à Trier par première colonne:

$sorter = new TableSorter(0); // sort by first column
$mdarray = $sorter->sort($mdarray);
31
répondu troelskn 2008-09-18 20:53:11

tri à rangées multiples utilisant une fermeture

Voici une autre approche utilisant uasort() et une fonction de rappel anonyme (fermeture). J'ai utilisé cette fonction régulièrement. PHP 5.3 requis " 151950920 – - plus de dépendances!

/**
 * 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
répondu feeela 2012-04-13 14:38:54

je sais que cela fait 2 ans que cette question a été posée et a reçu une réponse, mais voici une autre fonction qui trie un tableau bidimensionnel. Il accepte un nombre variable d'arguments, vous permettant de passer dans plus d'une clé (nom de colonne) pour trier. PHP 5.3 requis.

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;
}

Essayez ici: http://www.exorithm.com/algorithm/view/sort_multi_array

6
répondu Mike C 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
répondu Kamal 2017-08-24 07:51:05

la fonction "Usort" est votre réponse.

http://php.net/usort

3
répondu Jan Hančič 2012-03-13 18:43:22

Voici une classe php4/php5 qui va trier un ou plusieurs champs:

// 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
répondu Devon 2008-09-18 22:16:00

avant de pouvoir lancer la classe TableSorter, j'avais créé une fonction basée sur ce que Shinhan avait fourni.

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 est le tableau MD que vous voulez trier.
  • $colonne est la colonne que vous souhaitez trier.
  • $ méthode est la façon dont vous voulez que le tri effectué, tels que SORT_DESC
  • $ has_header est défini à true si la première ligne contient l'en-tête les valeurs que vous ne voulez pas triés.
0
répondu Melikoth 2017-05-23 12:03:04

j'ai essayé plusieurs réponses populaires array_multisort() et usort() et aucune d'entre elles n'a fonctionné pour moi. Les données sont mélangées et le code est illisible. Voici une solution rapide et sale. Attention: n'utilisez ceci que si vous êtes sûr qu'un délimiteur voyou ne reviendra pas vous hanter plus tard!

disons que chaque ligne dans votre tableau multi ressemble à: nom, 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);

vous voulez récupérer vos affaires par ordre alphabétique?

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

Oui, c'est sale. Mais super facile, ça ne te fera pas exploser la tête.

0
répondu PJ Brunet 2013-06-19 21:00:25

je préfère utiliser array_multisort. Voir la documentation ici .

-1
répondu Tim Boland 2013-01-03 03:59:30