Trier le tableau d'objets par champs d'objet

514

Comment puis-je trier ce tableau d'objets par l'un de ses champs, comme nameou count?

  Array
(
    [0] => stdClass Object
        (
            [ID] => 1
            [name] => Mary Jane
            [count] => 420
        )

    [1] => stdClass Object
        (
            [ID] => 2
            [name] => Johnny
            [count] => 234
        )

    [2] => stdClass Object
        (
            [ID] => 3
            [name] => Kathy
            [count] => 4354
        )

   ....
Alex
la source

Réponses:

699

Utilisez usort , voici un exemple adapté du manuel:

function cmp($a, $b) {
    return strcmp($a->name, $b->name);
}

usort($your_data, "cmp");

Vous pouvez également utiliser n'importe quel appelable comme deuxième argument. Voici quelques exemples:

  • Utiliser des fonctions anonymes (à partir de PHP 5.3)

    usort($your_data, function($a, $b) {return strcmp($a->name, $b->name);});
  • De l'intérieur d'une classe

    usort($your_data, array($this, "cmp")); // "cmp" should be a method in the class
  • Utilisation des fonctions fléchées (depuis PHP 7.4)

    usort($your_data, fn($a, $b) => strcmp($a->name, $b->name));

De plus, si vous comparez des valeurs numériques, la fn($a, $b) => $a->count - $b->countfonction "comparer" devrait faire l'affaire.

cambraca
la source
93
C'est très bien, mais si la fonction de tri est dans la même classe que la fonction appelante, vous devez utiliser: usort ($ your_data, array ($ this, "cmp"));
rmooney
7
@rmooney Oui, mais seulement si vous êtes dans une classe.
cambraca
11
mettez le premier commentaire de @rmooney dans votre réponse
Mohammad Faisal
7
Ou si votre fonction de comparaison se trouve dans votre modèle / objet que vous comparez (ce qui est un design plus propre à mon avis), vous devez inclure l'espace de noms complet à votre modèle / objet comme ceci: uasort ($ members, array ("Path \ to \ votre \ Model \ Member "," compareByName "));
clauziere
3
cela ne me rend rien rien trié, juste le plus gros en premier, et tout le reste je ne trie pas
Alberto Acuña
472

Voici une meilleure façon d'utiliser les fermetures

usort($your_data, function($a, $b)
{
    return strcmp($a->name, $b->name);
});

Veuillez noter que ce n'est pas dans la documentation de PHP mais si vous utilisez 5.3+, les fermetures sont prises en charge là où des arguments appelables peuvent être fournis.

Scott Quinlan
la source
16
J'adore celle-ci mieux que la réponse acceptée car nous pouvons rapidement définir la fonction de comparaison et l'utiliser dans une classe
Nam G VU
11
Si vous souhaitez conserver les clés du tableau, utilisezuasort()
gillytech
10
Pour trier desc,-1 * strcmp($a->name, $b->name);
Wallace Maxters
17
Pas besoin de multiplier pour trier les desc. strcmp($b->name, $a->name)
Échangez simplement les arguments
3
Vous pouvez rencontrer une situation, comme moi, où la réponse acceptée est préférable à celle-ci. Par exemple, vous pouvez avoir un parent et une classe enfant. La classe enfant remplace une fonction qui utilise usortmais la fonction de comparaison est la même. En utilisant cela, vous devrez dupliquer le code pour la fermeture au lieu d'appeler une protected staticméthode que vous ne devriez définir qu'une seule fois dans la classe parente.
Pere
48

Si vous souhaitez trier les valeurs entières:

// Desc sort
usort($array,function($first,$second){
    return $first->number < $second->number;
});

// Asc sort
usort($array,function($first,$second){
    return $first->number > $second->number;
});

MISE À JOUR avec la chaîne n'oubliez pas de convertir dans le même registre (supérieur ou inférieur)

// Desc sort
usort($array,function($first,$second){
    return strtolower($first->text) < strtolower($second->text);
});

// Asc sort
usort($array,function($first,$second){
    return strtolower($first->text) > strtolower($second->text);
});
Roman Yakoviv
la source
44

si vous utilisez php oop, vous devrez peut-être changer pour:

public static function cmp($a, $b) 
{
    return strcmp($a->name, $b->name);
}

//in this case FUNCTION_NAME would be cmp
usort($your_data, array('YOUR_CLASS_NAME','FUNCTION_NAME')); 
Doron Segal
la source
28
usort($array, 'my_sort_function');

var_dump($array);

function my_sort_function($a, $b)
{
    return $a->name < $b->name;
}

Le même code sera avec le countchamp.

Plus de détails sur usort: http://ru2.php.net/usort

Btw, d'où avez-vous obtenu ce tableau? J'espère que ce n'est pas de la base de données?

zerkms
la source
1
Contiendra $resulten fait TRUEsi c'est réussi, et votre comparaison devrait l'être $a->name > $b->name. :)
cambraca
2
@cambraca: oh, j'ai oublié qu'il accepte le tableau par référence. Btw, OP n'a pas dit de quel ordre il avait besoin pour trier la collection.
zerkms
1
Eh bien oui, c'est une base de données :) en fait à partir d'une fonction qui obtient les données de la base de données
Alex
3
@Alex: pourquoi ne le triez-vous pas dans la base de données alors? ORDER BY count
zerkms
1
c'est plus compliqué, car c'est une fonction standard de wordpress, et comme j'écris un plugin, je ne peux pas changer les fichiers wp. J'ai essayé votre exemple en utilisant create_function (parce que je l'utilise dans une classe et je ne sais pas comment passer le nom de la fonction à usort): create_function('$a,$b', "return $a->count < $b->count;")mais je ne peux pas le faire fonctionner :( je reçois quelques avis et avertissement que usort s'attend à ce que le paramètre 2 soit un rappel valide
Alex
9

Vous pouvez utiliser cette fonction (fonctionne en version PHP> = 5.3):

function sortArrayByKey(&$array,$key,$string = false,$asc = true){
    if($string){
        usort($array,function ($a, $b) use(&$key,&$asc)
        {
            if($asc)    return strcmp(strtolower($a{$key}), strtolower($b{$key}));
            else        return strcmp(strtolower($b{$key}), strtolower($a{$key}));
        });
    }else{
        usort($array,function ($a, $b) use(&$key,&$asc)
        {
            if($a[$key] == $b{$key}){return 0;}
            if($asc) return ($a{$key} < $b{$key}) ? -1 : 1;
            else     return ($a{$key} > $b{$key}) ? -1 : 1;

        });
    }
}

Exemple:

sortArrayByKey($yourArray,"name",true); //String sort (ascending order)
sortArrayByKey($yourArray,"name",true,false); //String sort (descending order)
sortArrayByKey($yourArray,"id"); //number sort (ascending order)
sortArrayByKey($yourArray,"count",false,false); //number sort (descending order)
PoengAlex
la source
j'ai utilisé $a->{$key} et $b->{$key}plutôt que $a[$key]et $b[$key]comme nous le sommes, à proprement parler, traitant des propriétés plutôt que des membres du tableau, mais c'était toujours la réponse que je cherchais.
SteJ
Veuillez implémenter la suggestion de @ SteJ dans l'exemple de code car la solution que vous donnez ne fonctionne que pour les objets simples, mais avec le correctif de SteJ, cela fonctionne pour tous les tableaux d'objets sur
lesquels
6

Vous pouvez utiliser usort, comme ceci:

usort($array,function($first,$second){
    return strcmp($first->name, $second->name);
});
Luca C.
la source
5

Si tout échoue, voici une autre solution:

$names = array(); 
foreach ($my_array as $my_object) {
    $names[] = $my_object->name; //any object field
}

array_multisort($names, SORT_ASC, $my_array);

return $my_array;
Adrian P.
la source
Vous devriez obtenir un Oscar pour cette solution! ))))) Thankyou
Imeksbank
4

L'inconvénient de toutes les réponses ici est qu'elles utilisent des noms de champs statiques , j'ai donc écrit une version ajustée dans le style OOP. En supposant que vous utilisez des méthodes getter, vous pouvez utiliser directement cette classe et utiliser le nom de champ comme paramètre . Quelqu'un le trouve probablement utile.

class CustomSort{

    public $field = '';

    public function cmp($a, $b)
    {
        /**
         * field for order is in a class variable $field
         * using getter function with naming convention getVariable() we set first letter to uppercase
         * we use variable variable names - $a->{'varName'} would directly access a field
         */
        return strcmp($a->{'get'.ucfirst($this->field)}(), $b->{'get'.ucfirst($this->field)}());
    }

    public function sortObjectArrayByField($array, $field)
    {
        $this->field = $field;
        usort($array, array("Your\Namespace\CustomSort", "cmp"));;
        return $array;
    }
} 
oshell
la source
3

si vous voulez trier les dates

   usort($threads,function($first,$second){
        return strtotime($first->dateandtime) < strtotime($second->dateandtime);
    });
Nicolas Giszpenc
la source
3

Une alternative simple qui vous permet de déterminer dynamiquement le champ sur lequel se base le tri:

$order_by = 'name';
usort($your_data, function ($a, $b) use ($order_by)
{
    return strcmp($a->{$order_by}, $b->{$order_by});
});

Ceci est basé sur la classe Closure , qui autorise les fonctions anonymes. Il est disponible depuis PHP 5.3.

clami219
la source
2

Si vous avez besoin d'une comparaison de chaînes locale, vous pouvez utiliser à la strcollplace de strcmp.

N'oubliez pas d'utiliser d'abord setlocaleavec LC_COLLATEpour définir les informations locales si nécessaire.

  usort($your_data,function($a,$b){
    setlocale (LC_COLLATE, 'pl_PL.UTF-8'); // Example of Polish language collation
    return strcoll($a->name,$b->name);
  });
Wilq
la source
2

Si vous utilisez ceci dans Codeigniter, vous pouvez utiliser les méthodes:

usort($jobs, array($this->job_model, "sortJobs"));  // function inside Model
usort($jobs, array($this, "sortJobs")); // Written inside Controller.

@rmooney merci pour la suggestion. Ça m'aide vraiment.

Développeur PHP
la source
En quoi ce codeigniter est-il spécifique?
ggdx
2

Merci pour les inspirations, j'ai aussi dû ajouter un paramètre $ translator externe

usort($listable_products, function($a, $b) {
    global $translator;
    return strcmp($a->getFullTitle($translator), $b->getFullTitle($translator));
});
michalzuber
la source
1

Si vous devez trier par un seul champ, usortc'est un bon choix. Cependant, la solution devient rapidement compliquée si vous devez trier par plusieurs champs. Dans ce cas, la bibliothèque YaLinqo * peut être utilisée, qui implémente une syntaxe de requête de type SQL pour les tableaux et les objets. Il a une jolie syntaxe pour tous les cas:

$sortedByName         = from($objects)->orderBy('$v->name');
$sortedByCount        = from($objects)->orderBy('$v->count');
$sortedByCountAndName = from($objects)->orderBy('$v->count')->thenBy('$v->name');

Voici '$v->count'un raccourci pour function ($v) { return $v->count; }(l'un ou l'autre peut être utilisé). Ces chaînes de méthodes renvoient des itérateurs, mais vous pouvez obtenir des tableaux en ajoutant ->toArray()à la fin si vous en avez besoin.

* développé par mes soins

Athari
la source
1

Vous pouvez utiliser la fonction triée de Nspl :

use function \nspl\a\sorted;
use function \nspl\op\propertyGetter;
use function \nspl\op\methodCaller;

// Sort by property value
$sortedByCount = sorted($objects, propertyGetter('count'));

// Or sort by result of method call
$sortedByName = sorted($objects, methodCaller('getName'));
Ihor Burlachenko
la source
Veuillez expliquer pourquoi l'OP aurait besoin d'une bibliothèque supplémentaire pour fournir une installation apparemment résolue par des fonctions
intégrées
1

C'est ce que j'ai pour une classe utilitaire

class Util
{
    public static function sortArrayByName(&$arrayToSort, $meta) {
        usort($arrayToSort, function($a, $b) use ($meta) {
            return strcmp($a[$meta], $b[$meta]);
        });
    }
}

Appeler:

Util::sortArrayByName($array, "array_property_name");
Demodave
la source
1

Vous pouvez utiliser usort comme ceci

Si vous souhaitez trier par numéro:

function cmp($a, $b)
{
    if ($a == $b) {
        return 0;
    }
    return ($a < $b) ? -1 : 1;
}

$a = array(3, 2, 5, 6, 1);

usort($a, "cmp");

Ou Abc char:

function cmp($a, $b)
{
    return strcmp($a["fruit"], $b["fruit"]);
}

$fruits[0]["fruit"] = "lemons";
$fruits[1]["fruit"] = "apples";
$fruits[2]["fruit"] = "grapes";

usort($fruits, "cmp");

Voir plus: https://www.php.net/manual/en/function.usort.php

Alex
la source