Ordina array di oggetti per campi oggetto


514

Come posso ordinare questo array di oggetti per uno dei suoi campi, come nameo 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
        )

   ....

Risposte:


699

Usa usort , ecco un esempio adattato dal manuale:

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

usort($your_data, "cmp");

Puoi anche usare qualsiasi callable come secondo argomento. Ecco alcuni esempi:

  • Usare funzioni anonime (da PHP 5.3)

    usort($your_data, function($a, $b) {return strcmp($a->name, $b->name);});
  • Dall'interno di una classe

    usort($your_data, array($this, "cmp")); // "cmp" should be a method in the class
  • Uso delle funzioni freccia (da PHP 7.4)

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

Inoltre, se stai confrontando valori numerici, fn($a, $b) => $a->count - $b->countpoiché la funzione "confronta" dovrebbe fare il trucco.


93
Questo è fantastico, ma se la funzione di ordinamento è nella stessa classe della funzione chiamante, dovresti usare: usort ($ your_data, array ($ this, "cmp"));
rmooney,

7
@rmooney Sì, ma solo se sei all'interno di una classe.
Cambraca,

11
inserisci il primo commento di @rmooney nella tua risposta
Mohammad Faisal,

7
O se la tua funzione di confronto è nel tuo modello / oggetto che stai confrontando (che è un design più pulito secondo me) devi includere l'intero spazio dei nomi per il tuo modello / oggetto in questo modo: uasort ($ members, array ("Path \ to \ your \ Model \ Member "," compareByName "));
Clauziere,

3
questo non mi restituisce nulla di ordinato, solo il più grande per primo, e tutto il resto non lo torto
Alberto Acuña,

472

Ecco un modo migliore usando le chiusure

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

Nota che questo non è nella documentazione di PHP ma se usi le chiusure 5.3+ sono supportate dove è possibile fornire argomenti richiamabili.


16
Adoro questo meglio della risposta accettata poiché possiamo definire rapidamente la funzione di confronto e possiamo usare in una classe
Nam G VU

11
Se si desidera conservare le chiavi dell'array, utilizzareuasort()
gillytech

10
Per l'ordinamento desc,-1 * strcmp($a->name, $b->name);
Wallace Maxters,

17
Non è necessario moltiplicare per ordinare desc. Basta scambiare args:strcmp($b->name, $a->name)
zxcat

3
Potresti incontrare una situazione, come me, in cui la risposta accettata è preferibile a questa. Ad esempio, potresti avere un genitore e una classe figlio. La classe figlio sostituisce una funzione che utilizza usortma la funzione di confronto è la stessa. Usando questo, dovresti duplicare il codice per la chiusura invece di fare una chiamata a un protected staticmetodo che dovresti definire solo una volta nella classe genitore.
Pere,

48

Se si desidera ordinare i valori interi:

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

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

AGGIORNATO con la stringa non dimenticare di convertire nello stesso registro (superiore o inferiore)

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

44

se stai usando php oop potresti dover cambiare in:

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

28
usort($array, 'my_sort_function');

var_dump($array);

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

Lo stesso codice sarà con il countcampo.

Maggiori dettagli su usort: http://ru2.php.net/usort

A proposito, da dove hai preso quell'array? Spero che non dal database?


1
In realtà $resultconterrà TRUEse ha esito positivo e il tuo confronto dovrebbe esserlo $a->name > $b->name. :)
Cambraca,

2
@cambraca: oh, ho dimenticato che accetta array per riferimento. A proposito, OP non ha detto quale ordine ha bisogno per ordinare la raccolta.
zerkms,

1
bene sì, è un database :) in realtà da una funzione che ottiene i dati dal database
Alex

3
@Alex: perché non lo ordini nel database allora? ORDER BY count
zerkms,

1
è più complicato, perché fa parte della funzione stadard di wordpress e, mentre scrivo un plugin, non posso cambiare i file wp. Ho provato il tuo esempio usando create_function (perché lo sto usando all'interno di una classe e non so come passare il nome della funzione per usort): create_function('$a,$b', "return $a->count < $b->count;")ma non riesco a farlo funzionare :( ricevo alcuni avvisi e avviso che usort prevede che il parametro 2 sia un callback valido
Alex,

9

Puoi usare questa funzione (funziona in versione 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;

        });
    }
}

Esempio:

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)

Ho usato $a->{$key}e $b->{$key}, piuttosto che $a[$key]e $b[$key]come siamo, a rigor di termini, si tratta di proprietà, piuttosto che membri di array, ma questo era ancora la risposta che cercavo.
SteJ

Si prega di implementare il suggerimento di @Stej nel codice di esempio poiché la soluzione fornita funziona solo per oggetti semplici ma con la correzione di Stej funziona per tutti gli array di oggetti su cui l'ho provato
user2993145

6

Puoi usare usort, in questo modo:

usort($array,function($first,$second){
    return strcmp($first->name, $second->name);
});

5

Se tutto fallisce qui c'è un'altra soluzione:

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

Dovresti ottenere un Oscar per questa soluzione! ))))) Grazie
Imeksbank il

4

L'aspetto negativo di tutte le risposte qui è che usano nomi di campi statici , quindi ho scritto una versione modificata in stile OOP. Supponendo che si stiano utilizzando metodi getter, è possibile utilizzare direttamente questa classe e utilizzare il nome del campo come parametro . Probabilmente qualcuno lo trova 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;
    }
} 

3

se vuoi ordinare le date

   usort($threads,function($first,$second){
        return strtotime($first->dateandtime) < strtotime($second->dateandtime);
    });

3

Una semplice alternativa che consente di determinare dinamicamente il campo su cui si basa l'ordinamento:

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

Questo si basa sulla classe Closure , che consente funzioni anonime. È disponibile da PHP 5.3.


2

Se è necessario un confronto di stringhe basato su locale, è possibile utilizzare strcollinvece di strcmp.

Ricordatevi di primo utilizzo setlocalecon LC_COLLATEal set di informazioni sulla locale se necessario.

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

2

Se si utilizza questo all'interno di Codeigniter, è possibile utilizzare i metodi:

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

@rmooney grazie per il suggerimento. Mi aiuta davvero.


Come è esattamente questo Codeigniter specifico?
ggdx,

2

Grazie per le ispirazioni, ho anche dovuto aggiungere un parametro $ traduttore esterno

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

1

Se devi ordinare solo per un campo, allora usortè una buona scelta. Tuttavia, la soluzione diventa rapidamente disordinata se è necessario ordinare in base a più campi. In questo caso, è possibile utilizzare la libreria YaLinqo *, che implementa una sintassi di query simile a SQL per matrici e oggetti. Ha una sintassi abbastanza per tutti i casi:

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

Qui, '$v->count'è una scorciatoia per function ($v) { return $v->count; }(entrambi possono essere utilizzati). Queste catene di metodi restituiscono iteratori, ma puoi ottenere array aggiungendo ->toArray()alla fine se ne hai bisogno.

* sviluppato da me


1

È possibile utilizzare la funzione ordinata da 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'));

Spiega perché l'OP avrebbe bisogno di un'intera libreria aggiuntiva per fornire una struttura apparentemente risolta da funzioni
integrate

1

Questo è quello che ho per una classe di utilità

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

Chiamalo:

Util::sortArrayByName($array, "array_property_name");

1

Puoi usare usort in questo modo

Se vuoi ordinare per numero:

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

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

usort($a, "cmp");

O 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");

Vedi di più: https://www.php.net/manual/en/function.usort.php

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.