Сортувати масив об’єктів за полями об’єктів


514

Як можна сортувати цей масив об’єктів за одним із його полів, як-от nameабо 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
        )

   ....

Відповіді:


699

Використовуйте usort , ось приклад, адаптований з посібника:

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

usort($your_data, "cmp");

Ви також можете використовувати будь-який дзвінок як другий аргумент. Ось кілька прикладів:

  • Використання анонімних функцій (від PHP 5.3)

    usort($your_data, function($a, $b) {return strcmp($a->name, $b->name);});
  • Зсередини класу

    usort($your_data, array($this, "cmp")); // "cmp" should be a method in the class
  • Використання функцій стрілок (від PHP 7.4)

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

Крім того, якщо ви порівнюєте числові значення, fn($a, $b) => $a->count - $b->countоскільки функція "порівняння" повинна зробити свою справу.


93
Це чудово, але якщо функція сортування знаходиться в тому ж класі, що і функція виклику, слід використовувати: usort ($ your_data, array ($ this, "cmp"));
rmooney

7
@rmooney Так, але тільки якщо ви знаходитесь у класі.
cambraca

11
поставте перший коментар від @rmooney у своїй відповіді
Мохаммед Файсал

7
Або якщо функція порівняння знаходиться у вашій моделі / об’єкті, який ви порівнюєте (що, на мою думку, є більш чистим дизайном), ви повинні включити повну область імен до вашої моделі / об’єкта, як це: uasort ($ members, array ("Шлях \ до" \ ваша \ Модель \ Член "," ПорівнятиByName "));
clauziere

3
це не повертає мені нічого розсортованого, лише найголовніший перший, а все інше я несподіваний
Альберто Акунья

472

Ось приємніший спосіб використання закривань

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

Зверніть увагу, це не в документації PHP, але якщо ви використовуєте 5.3+ закриття, які підтримуються там, де можна навести аргументи для виклику.


16
Мені подобається ця краща, ніж прийнята відповідь, оскільки ми можемо швидко визначити функцію порівняння та можемо використовувати в класі
Nam G VU

11
Якщо ви хочете зберегти ключі масиву, використовуйтеuasort()
gillytech

10
Для -1 * strcmp($a->name, $b->name);
дескрипції

17
Не потрібно множувати, щоб сортувати дес. Просто поміняйте аргументи:strcmp($b->name, $a->name)
zxcat

3
Ви можете зіткнутися з ситуацією, як я, коли прийнята відповідь є кращою перед цією. Наприклад, у вас можуть бути батьки та дочірній клас. Дочірній клас переосмислює функцію, яка використовується, usortале функція порівняння однакова. Використовуючи це, вам потрібно буде дублювати код для закриття, а не робити виклик protected staticметоду, який потрібно було б визначити лише один раз у батьківському класі.
Пер

48

Якщо ви хочете сортувати цілі значення:

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

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

ОНОВЛЕНО за допомогою рядка, не забудьте перетворити в один і той же реєстр (верхній або нижній)

// 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

якщо ви використовуєте php oop, можливо, вам доведеться змінити на:

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

Той самий код буде і з countполем.

Детальніше про usort: http://ru2.php.net/usort

До речі, звідки ти взяв цей масив? Я сподіваюся, що не з бази даних?


1
Насправді $resultбуде містити, TRUEякщо це буде успішно, і ваше порівняння повинно бути $a->name > $b->name. :)
cambraca

2
@cambraca: о, забув, він приймає масив за посиланням. Btw, OP не сказала, для якого замовлення йому потрібно сортувати колекцію.
zerkms

1
ну так, це база даних :) насправді від функції, яка отримує дані з бази даних
Алекс

3
@Alex: чому б тоді не сортувати його в базі даних? ORDER BY count
zerkms

1
це складніше, тому що це частина функції стаддарду WordPress, і оскільки я пишу плагін, я не можу змінювати файли wp. Я спробував ваш приклад, використовуючи create_function (тому що я використовую його всередині класу, і я не знаю, як передати ім'я функції усортовано): create_function('$a,$b', "return $a->count < $b->count;")але я не можу змусити його працювати :( я отримую кілька повідомлень і попереджаю, що usort очікує, що параметр 2 буде дійсним зворотним дзвоном
Алекс

9

Ви можете використовувати цю функцію (працює у версії 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;

        });
    }
}

Приклад:

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)

Я використовував $a->{$key}і $b->{$key}замість того , $a[$key]і $b[$key]як ми, строго кажучи, справа з властивостями , а не членів масиву, але це було до цих пір відповіді я шукав.
SteJ

Будь ласка, реалізуйте пропозицію @ SteJ у прикладі коду, оскільки рішення, яке ви надаєте, працює лише для простих об'єктів, але за допомогою виправлення SteJ він працює для всіх масивів об'єктів, на яких я його спробував
user2993145

6

Ви можете використовувати usort, як це:

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

5

Якщо тут все не вдається, є інше рішення:

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

Ви повинні отримати Оскар за це рішення! ))))) Дякую
Імексбанк

4

Недоліком усіх відповідей тут є те, що вони використовують статичні імена полів, тому я написав скориговану версію в стилі OOP. Припускаючи, що ви використовуєте методи getter, ви можете безпосередньо використовувати цей клас і використовувати ім'я поля як параметр . Напевно, хтось вважає це корисним.

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

якщо ви хочете сортувати дати

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

3

Проста альтернатива, яка дозволяє динамічно визначати поле, на якому ґрунтується сортування:

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

На цьому ґрунтується клас закриття , який дозволяє анонімні функції. Він доступний з PHP 5.3.


2

Якщо вам потрібно місцеве порівняння рядків на основі, ви можете використовувати strcollзамість strcmp.

Remeber першого використання setlocaleз LC_COLLATEдо встановленої інформації локалі , якщо це необхідно.

  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

Якщо ви використовуєте це всередині Codeigniter, ви можете використовувати методи:

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

@rmooney дякую за пропозицію. Мені це справді допомагає.


Наскільки конкретно цей Codeigniter?
ggdx

2

Дякую за натхнення, мені також довелося додати параметр зовнішнього $ перекладача

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

1

Якщо вам потрібно сортувати лише одне поле, то usortце хороший вибір. Однак рішення швидко стає безладним, якщо потрібно сортувати за кількома полями. У цьому випадку може використовуватися бібліотека YaLinqo *, яка реалізує SQL-подібний синтаксис запитів для масивів та об'єктів. Він має синтаксис для всіх випадків:

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

Ось '$v->count'скорочення для function ($v) { return $v->count; }(або можна використовувати). Ці ланцюжки методів повертають ітератори, але ви можете отримати масиви, додавши ->toArray()в кінці, якщо вам це потрібно.

* розроблений мною


1

Ви можете використовувати відсортовану функцію від 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'));

Будь ласка, поясніть, чому ОП потребуватиме цілу додаткову бібліотеку, щоб забезпечити споруду, здавалося б, вирішеною вбудованими функціями
ggdx

1

Це те, що я маю для корисного класу

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

Назви це:

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

1

Ви можете використовувати usort, як це

Якщо ви хочете сортувати за номером:

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

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

usort($a, "cmp");

Або 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");

Дивіться більше: https://www.php.net/manual/en/function.usort.php

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.