web-dev-qa-db-fra.com

Comment trier un tableau de tableaux associatifs par valeur d'une clé donnée en PHP?

Étant donné ce tableau:

$inventory = array(

   array("type"=>"fruit", "price"=>3.50),
   array("type"=>"milk", "price"=>2.90),
   array("type"=>"pork", "price"=>5.43),

);

Je voudrais trier les éléments de $inventory par prix pour obtenir:

$inventory = array(

   array("type"=>"pork", "price"=>5.43),
   array("type"=>"fruit", "price"=>3.50),
   array("type"=>"milk", "price"=>2.90),

);

Comment puis-je faire ceci?

344
Matt

Vous avez raison, la fonction que vous recherchez est array_multisort() .

Voici un exemple tiré directement du manuel et adapté à votre cas:

$price = array();
foreach ($inventory as $key => $row)
{
    $price[$key] = $row['price'];
}
array_multisort($price, SORT_DESC, $inventory);
482
Josh Davis

PHP 7+

À partir de PHP 7, cela peut être fait de manière concise en utilisant usort avec une fonction anonyme qui utilise l'opérateur vaisseau spatial pour comparer des éléments.

Vous pouvez faire un tri croissant comme ceci:

usort($inventory, function ($item1, $item2) {
    return $item1['price'] <=> $item2['price'];
});

Ou un type descendant comme ceci:

usort($inventory, function ($item1, $item2) {
    return $item2['price'] <=> $item1['price'];
});

Pour comprendre comment cela fonctionne, notez que usort prend une fonction de comparaison fournie par l'utilisateur qui doit se comporter comme suit (à partir de la documentation):

La fonction de comparaison doit renvoyer un entier inférieur, égal ou supérieur à zéro si le premier argument est considéré comme étant respectivement inférieur, égal ou supérieur au second.

Et notez également que <=>, l'opérateur du vaisseau spatial,

renvoie 0 si les deux opérandes sont égaux, 1 si la gauche est plus grande et -1 si la droite est plus grande

c'est exactement ce dont usort a besoin. En fait, presque toute la justification donnée pour ajouter <=> à la langue dans https://wiki.php.net/rfc/combined-comparison-operator est que c'est

simplifie la rédaction des rappels de commande à utiliser avec usort()


PHP 5.3+

PHP 5.3 a introduit les fonctions anonymes, mais n'a pas encore d'opérateur de vaisseau spatial. Nous pouvons toujours utiliser usort pour trier notre tableau, mais c'est un peu plus détaillé et plus difficile à comprendre:

usort($inventory, function ($item1, $item2) {
    if ($item1['price'] == $item2['price']) return 0;
    return $item1['price'] < $item2['price'] ? -1 : 1;
});

Notez que s'il est assez courant que les comparateurs traitant des valeurs entières renvoient simplement la différence des valeurs, comme $item2['price'] - $item1['price'], nous ne peut pas le faire en toute sécurité dans ce cas. En effet, les prix sont des nombres à virgule flottante dans l'exemple du demandeur de question, mais la fonction de comparaison à laquelle nous passons à usort doit renvoyer des entiers pour que usort fonctionne correctement:

Le renvoi de valeurs non entières} _ à partir de la fonction de comparaison, telles que float, entraînera une conversion interne en entier de la valeur renvoyée par le rappel. Ainsi, des valeurs telles que 0,99 et 0,1 seront toutes deux converties en une valeur entière égale à 0, ce qui comparera ces valeurs comme étant égales.

C’est un piège important à garder à l’esprit lorsque vous utilisez usort dans PHP 5.x! Ma version originale de cette réponse fait cette erreur et pourtant j'ai accumulé dix votes positifs sur des milliers de vues apparemment sans que personne ne remarque le bug sérieux. La facilité avec laquelle des ratés comme moi peuvent gâcher les fonctions de comparaison est la raison pour laquelle {précisément} l'opérateur de vaisseau spatial, plus facile à utiliser, a été ajouté au langage dans PHP 7.

231
Mark Amery

Alors que d'autres ont correctement suggéré l'utilisation de array_multisort(), pour une raison quelconque, aucune réponse ne semble reconnaître l'existence de array_column(), ce qui peut grandement simplifier la solution. Donc, ma suggestion serait:

array_multisort(array_column($inventory, 'price'), SORT_DESC, $inventory);
53
Mariano Iglesias

Puisque vos éléments de tableau sont eux-mêmes des tableaux avec des clés de chaîne, le mieux est de définir une fonction de comparaison personnalisée. C'est assez rapide et facile à faire. Essaye ça:

function invenDescSort($item1,$item2)
{
    if ($item1['price'] == $item2['price']) return 0;
    return ($item1['price'] < $item2['price']) ? 1 : -1;
}
usort($inventory,'invenDescSort');
print_r($inventory);

Produit ce qui suit:

Array
(
    [0] => Array
        (
            [type] => pork
            [price] => 5.43
        )

    [1] => Array
        (
            [type] => fruit
            [price] => 3.5
        )

    [2] => Array
        (
            [type] => milk
            [price] => 2.9
        )

)
39
zombat

J'ai fini sur ceci:

function sort_array_of_array(&$array, $subfield)
{
    $sortarray = array();
    foreach ($array as $key => $row)
    {
        $sortarray[$key] = $row[$subfield];
    }

    array_multisort($sortarray, SORT_ASC, $array);
}

Appelez simplement la fonction en passant le tableau et le nom du champ du tableau de second niveau .

sort_array_of_array($inventory, 'price');
21
Danielzt

Vous pouvez utiliser usort avec une fonction anonyme, par exemple.

usort($inventory, function ($a, $b) { return strnatcmp($a['price'], $b['price']); });
13
kenorb
$inventory = 
    array(array("type"=>"fruit", "price"=>3.50),
          array("type"=>"milk", "price"=>2.90),
          array("type"=>"pork", "price"=>5.43),
          );

function pricesort($a, $b) {
  $a = $a['price'];
  $b = $b['price'];
  if ($a == $b)
    return 0;
  return ($a > $b) ? -1 : 1;
}

usort($inventory, "pricesort");
// uksort($inventory, "pricesort");

print("first: ".$inventory[0]['type']."\n\n");
// for usort(): prints milk (item with lowest price)
// for uksort(): prints fruit (item with key 0 in the original $inventory)

// foreach prints the same for usort and uksort.
foreach($inventory as $i){
  print($i['type'].": ".$i['price']."\n");
}

les sorties:

first: pork

pork: 5.43
fruit: 3.5
milk: 2.9
8
danamlund

A été testé sur 100 000 enregistrements: Temps en secondes (calculé par funciton microtime) .Seulement pour les valeurs uniques sur le tri des positions clés.

Solution de fonction de @Josh Davis: Temps passé: 1.5768740177155

Solution de mine: Temps passé: 0.094044923782349

Solution:

function SortByKeyValue($data, $sortKey, $sort_flags=SORT_ASC)
{
    if (empty($data) or empty($sortKey)) return $data;

    $ordered = array();
    foreach ($data as $key => $value)
        $ordered[$value[$sortKey]] = $value;

    ksort($ordered, $sort_flags);

    return array_values($ordered); *// array_values() added for identical result with multisort*
}
3
Nefelim

De Trier un tableau de tableaux associatifs par valeur de clé donnée en php :

uasort ( http://php.net/uasort ) vous permet de trier un tableau selon votre propre fonction définie. Dans votre cas, c'est simple:

$array = array(
  array('price'=>'1000.50','product'=>'test1'),
  array('price'=>'8800.50','product'=>'test2'),
  array('price'=>'200.0','product'=>'test3')
);

function cmp($a, $b) {
  return $a['price'] > $b['price'];
}

uasort($array, "cmp");
2
Kamal

Cette fonction est réutilisable:

function usortarr(&$array, $key, $callback = 'strnatcasecmp') {
    uasort($array, function($a, $b) use($key, $callback) {
        return call_user_func($callback, $a[$key], $b[$key]);
    });
}

Cela fonctionne bien sur les valeurs de chaîne par défaut, mais vous devrez sous-rappeler une fonction de comparaison nombre si toutes vos valeurs sont des nombres.

1
mpen

Vous pouvez essayer de définir votre propre fonction de comparaison, puis utiliser usort .

1
Alex Sexton
//Just in one line custom function
function cmp($a, $b)
{
return (float) $a['price'] < (float)$b['price'];
}
@uasort($inventory, "cmp");
print_r($inventory);

//result

Array
(
[2] => Array
    (
        [type] => pork
        [price] => 5.43
    )

[0] => Array
    (
        [type] => fruit
        [price] => 3.5
    )

[1] => Array
    (
        [type] => milk
        [price] => 2.9
    )

)
0
Kamal