Сортировать массив объектов по полям объекта

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
        )

   ....
Alex
источник

Ответы:

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функция «сравнения» должна помочь.

cambraca
источник
93
Это замечательно, но если функция сортировки находится в том же классе, что и вызывающая функция, вы должны использовать: usort ($ your_data, array ($ this, "cmp"));
rmooney
7
@rmooney Да, но только если ты в классе.
Cambraca
11
поместите первый комментарий @rmooney в свой ответ
Мохаммад Фейсал
7
Или, если ваша функция сравнения находится в вашей модели / объекте, который вы сравниваете (на мой взгляд, это более чистый дизайн), вы должны включить полное пространство имен в вашу модель / объект следующим образом: uasort ($ members, array ("Path \ to \ your \ Model \ Member "," compareByName "));
Clauziere
3
это не возвращает мне ничего не отсортировано, только самый большой сначала, и все остальное я сортирую
Альберто Acuña
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
Не нужно умножать для сортировки desc. Просто поменяйте местами аргументы: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

Кстати, откуда вы взяли этот массив? Я надеюсь, что не из базы данных?

zerkms
источник
1
На самом деле $resultбудет содержать, TRUEесли это успешно, и ваше сравнение должно быть $a->name > $b->name. :)
Cambraca
2
@ cambraca: о, забыл, что принимает массив по ссылке. Кстати, ОП не сказал, в каком порядке ему нужно сортировать коллекцию.
zerkms
1
ну да, это база данных :) фактически из функции, которая получает данные из базы данных
Alex
3
@ Алекс: почему бы тебе не отсортировать его в базе данных? ORDER BY count
zerkms
1
это сложнее, потому что это стандартная функция WordPress, и, поскольку я пишу плагин, я не могу изменить wp-файлы. Я попробовал ваш пример, используя create_function (потому что я использую его внутри класса, и я не знаю, как передать имя функции usort): create_function('$a,$b', "return $a->count < $b->count;")но я не могу заставить его работать :( Я получаю несколько уведомлений и предупреждений, что usort ожидает, что параметр 2 будет действительным обратным вызовом
Alex
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)
PoengAlex
источник
Я использовал $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

Недостатком всех ответов здесь является то, что они используют статические имена полей, поэтому я написал скорректированную версию в стиле ООП. Предполагая, что вы используете методы получения, вы можете напрямую использовать этот класс и использовать имя поля в качестве параметра . Возможно, кто-то найдет это полезным.

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
источник
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});
});

Это основано на классе Closure , который разрешает анонимные функции. Он доступен начиная с PHP 5.3.

clami219
источник
2

Если вам нужно локальное сравнение строк, вы можете использовать strcollвместо strcmp.

Запомните сначала использовать 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);
  });
Wilq
источник
2

Если вы используете это внутри Codeigniter, вы можете использовать методы:

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

@rmooney спасибо за предложение. Это действительно помогает мне.

PHP разработчик
источник
Насколько конкретно этот Codeigniter специфичен?
ggdx
2

Спасибо за вдохновение, мне также пришлось добавить внешний параметр $ transator

usort($listable_products, function($a, $b) {
    global $translator;
    return strcmp($a->getFullTitle($translator), $b->getFullTitle($translator));
});
michalzuber
источник
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'));
Игорь Бурлаченко
источник
Пожалуйста, объясните, почему OP потребуется целая дополнительная библиотека, чтобы обеспечить средство, казалось бы, разрешаемое встроенными функциями
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");
Demodave
источник
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

Alex
источник