Sortuj tablicę obiektów według pól obiektów

514

Jak mogę posortować tę tablicę obiektów według jednego z jej pól, takich jak namelub 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
źródło

Odpowiedzi:

698

Użyj usort , oto przykład zaadaptowany z instrukcji:

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

usort($your_data, "cmp");

Można też użyć dowolnego wpłacone jako drugi argument. Oto kilka przykładów:

  • Korzystanie z anonimowych funkcji (z PHP 5.3)

    usort($your_data, function($a, $b) {return strcmp($a->name, $b->name);});
  • Od wewnątrz klasy

    usort($your_data, array($this, "cmp")); // "cmp" should be a method in the class
  • Korzystanie z funkcji strzałek (z PHP 7.4)

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

Ponadto, jeśli porównujesz wartości liczbowe, fn($a, $b) => $a->count - $b->countfunkcja „porównaj” powinna załatwić sprawę.

Cambraca
źródło
93
Jest to świetne, ale jeśli funkcja sortowania należy do tej samej klasy co funkcja wywołująca, powinieneś użyć: usort ($ twoja_data, tablica ($ this, "cmp"));
rmooney
7
@rmooney Tak, ale tylko jeśli jesteś w klasie.
cambraca
11
umieść pierwszy komentarz @rmooney w swojej odpowiedzi
Mohammad Faisal
7
Lub jeśli twoja funkcja porównywania znajduje się w twoim modelu / obiekcie, który porównujesz (co jest moim zdaniem czystszym projektem), musisz dołączyć pełną przestrzeń nazw do swojego modelu / obiektu w ten sposób: uasort ($ Members, tablica ("Path \ to \ your \ Model \ Member ”,„ CompareByName ”));
clauziere
3
to nie zwraca mi niczego posortowanego, tylko największe, a resztę nie sortuję
Alberto Acuña
472

Jest lepszy sposób przy użyciu zamknięć

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

Pamiętaj, że nie ma tego w dokumentacji PHP, ale jeśli używasz zamków w wersji 5.3+, obsługiwane są argumenty, które można wywoływać.

Scott Quinlan
źródło
16
Uwielbiam ten lepszy niż zaakceptowana odpowiedź, ponieważ możemy szybko zdefiniować funkcję porównywania i użyć jej w klasie
Nam G VU
11
Jeśli chcesz zachować klucze tablicy, użyjuasort()
gillytech
10
Dla sortowania,-1 * strcmp($a->name, $b->name);
Wallace Maxters
17
Nie ma potrzeby mnożenia, aby posortować desc. Wystarczy zamienić argumenty:strcmp($b->name, $a->name)
zxcat,
3
Możesz napotkać sytuację, taką jak ja, w której zaakceptowana odpowiedź jest lepsza niż ta. Na przykład możesz mieć klasę nadrzędną i podrzędną. Klasa potomna przesłania funkcję, która używa, usortale funkcja porównania jest taka sama. Korzystając z tego, powinieneś zduplikować kod zamknięcia zamiast wywoływać protected staticmetodę, którą musisz zdefiniować tylko raz w klasie nadrzędnej.
Pere
48

Jeśli chcesz posortować wartości całkowite:

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

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

ZAKTUALIZOWANO z ciągiem nie zapomnij przekonwertować na ten sam rejestr (górny lub dolny)

// 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 Jakowów
źródło
44

jeśli używasz php oop, być może będziesz musiał zmienić na:

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
źródło
28
usort($array, 'my_sort_function');

var_dump($array);

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

Ten sam kod będzie z countpolem.

Więcej informacji na temat usort: http://ru2.php.net/usort

Przy okazji, skąd masz tę tablicę? Mam nadzieję, że nie z bazy danych?

zerkms
źródło
1
Właściwie $resultbędzie zawierać, TRUEjeśli się powiedzie, a twoje porównanie powinno być $a->name > $b->name. :)
cambraca
2
@cambraca: och, zapomniałem, że przyjmuje tablicę przez odniesienie. Btw, OP nie powiedział, jaki porządek musi sortować.
zerkms
1
no tak, to baza danych :) właściwie z funkcji, która pobiera dane z bazy danych
Alex
3
@Alex: dlaczego więc nie posortujesz go w bazie danych? ORDER BY count
zerkms
1
jest to bardziej skomplikowane, ponieważ jest to część funkcji stadard w wordpress, a kiedy piszę wtyczkę, nie mogę zmieniać plików wp. Wypróbowałem twój przykład przy użyciu funkcji create_function (ponieważ używam go w klasie i nie wiem, jak przekazać nazwę funkcji do usortowania): create_function('$a,$b', "return $a->count < $b->count;")ale nie mogę sprawić, by działała :( Otrzymuję kilka powiadomień i ostrzeżeń, że usort oczekuje, że parametr 2 będzie prawidłowym wywołaniem zwrotnym
Alex
9

Możesz użyć tej funkcji (działa w wersji 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;

        });
    }
}

Przykład:

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
źródło
Użyłem, $a->{$key}a $b->{$key}zamiast tego, $a[$key]a $b[$key]tak naprawdę, zajmując się właściwościami, a nie elementami tablicy, ale to wciąż była odpowiedź, której szukałem.
SteJ
Proszę zaimplementować sugestię @ SteJ w przykładowym kodzie, ponieważ podane rozwiązanie działa tylko dla prostych obiektów, ale z poprawką SteJ działa dla wszystkich tablic obiektów, na których wypróbowałem
user2993145
6

Możesz użyć usorttakiego:

usort($array,function($first,$second){
    return strcmp($first->name, $second->name);
});
Luca C.
źródło
5

Jeśli wszystko się nie powiedzie, jest inne rozwiązanie:

$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.
źródło
Powinieneś dostać Oscara za to rozwiązanie! ))))) Thankyou
Imeksbank 25.07.19
4

Minusem wszystkich odpowiedzi tutaj jest to, że używają statycznych nazw pól, więc napisałem poprawioną wersję w stylu OOP. Zakładając, że używasz metod pobierających, możesz bezpośrednio użyć tej klasy i użyć nazwy pola jako parametru . Prawdopodobnie ktoś uzna to za przydatne.

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
źródło
3

jeśli chcesz posortować daty

   usort($threads,function($first,$second){
        return strtotime($first->dateandtime) < strtotime($second->dateandtime);
    });
Nicolas Giszpenc
źródło
3

Prosta alternatywa, która pozwala dynamicznie określić pole, na którym opiera się sortowanie:

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

Jest to oparte na klasie Closure , która umożliwia anonimowe funkcje. Jest dostępny od PHP 5.3.

clami219
źródło
2

Jeśli potrzebujesz lokalnego porównania ciągów, możesz użyć strcollzamiast strcmp.

Pamiętaj, aby po raz pierwszy użyć setlocalew LC_COLLATEcelu ustawienia informacji regionalnych w razie potrzeby.

  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
źródło
2

Jeśli używasz tego w Codeigniter, możesz użyć metod:

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

@rmooney dziękuję za sugestię. To naprawdę mi pomaga.

Programista PHP
źródło
Jak dokładnie ten Codeigniter jest specyficzny?
ggdx,
2

Dzięki za inspiracje musiałem również dodać zewnętrzny parametr $ translator

usort($listable_products, function($a, $b) {
    global $translator;
    return strcmp($a->getFullTitle($translator), $b->getFullTitle($translator));
});
Michalzuber
źródło
1

Jeśli chcesz sortować według tylko jednego pola, usortto dobry wybór. Jednak rozwiązanie szybko staje się niechlujne, jeśli trzeba sortować według wielu pól. W takim przypadku można użyć biblioteki YaLinqo *, która implementuje składnię zapytań podobną do SQL dla tablic i obiektów. Ma ładną składnię dla wszystkich przypadków:

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

Tutaj '$v->count'jest skrótem function ($v) { return $v->count; }(można użyć obu). Te łańcuchy metod zwracają iteratory, ale tablice można uzyskać, dodając->toArray() w końcu, jeśli potrzebujesz.

* opracowane przeze mnie

Athari
źródło
1

Możesz użyć posortowanej funkcji z 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
źródło
Wyjaśnij, dlaczego OP potrzebowałby całej dodatkowej biblioteki, aby zapewnić narzędzie pozornie rozwiązane przez wbudowane funkcje
ggdx 10.08.18
1

To właśnie mam dla klasy użytkowej

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

Nazwać:

Util::sortArrayByName($array, "array_property_name");
Demodave
źródło
1

Możesz użyć usort w ten sposób

Jeśli chcesz sortować według numeru:

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

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

usort($a, "cmp");

Lub znak ABC:

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

Zobacz więcej: https://www.php.net/manual/en/function.usort.php

Alex
źródło