657

How can I sort this array of objects by one of its fields, like name or 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
        )

   ....
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Alex
  • 66,732
  • 177
  • 439
  • 641

23 Answers23

923

Use usort, here's an example adapted from the manual:

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

usort($your_data, "cmp");

You can also use any callable as the second argument. Here are some examples:

  • Using anonymous functions (from PHP 5.3)

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

      usort($your_data, array($this, "cmp")); // "cmp" should be a method in the class
    
  • Using arrow functions (from PHP 7.4)

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

Also, if you're comparing numeric values, fn($a, $b) => $a->count - $b->count as the "compare" function should do the trick, or, if you want yet another way of doing the same thing, starting from PHP 7 you can use the Spaceship operator, like this: fn($a, $b) => $a->count <=> $b->count.

cambraca
  • 27,014
  • 16
  • 68
  • 99
  • 101
    This is great, but if the sorting function is in the same class as the calling function, you should use: usort($your_data, array($this, "cmp")); – rmooney Sep 20 '13 at 20:53
  • 7
    @rmooney Yes, but only if you're inside a class. – cambraca Sep 20 '13 at 22:37
  • 12
    put the [first comment by @rmooney](http://stackoverflow.com/questions/4282413/php-sort-array-of-objects-by-object-fields#comment27943356_4282423) in your answer – Mohammad Faisal Feb 18 '14 at 12:57
  • 7
    Or if your compare function is in your model/object that you are comparing (which is a cleaner design in my opinion) you must include the full namespace to your model/object like this: uasort($members, array("Path\to\your\Model\Member", "compareByName")); – clauziere Apr 04 '14 at 19:04
  • 3
    this dont return me nothing sorted, just the most big first, and all rest i unsort – Alberto Acuña Oct 22 '16 at 12:21
  • 3
    One liner: usort($your_data, function ($a, $b) { return strcmp($a->name, $b->name); }); – Luís Henriques Feb 01 '19 at 13:01
  • 2
    @clauziere's solution will work for static methods too, which in my opinion is the right type for a compare function. – Bertie92 Jul 14 '19 at 21:32
  • 2
    From php 7.4, usort($items, fn($a, $b) => strcmp($a->name, $b->name); – AndrewRMillar Sep 05 '19 at 06:54
  • How can I make this case-insensitive? "S" is not equal to "s", but I'd like it to be. (Example: wherein object name is a person's name, which happens to have been entered all in lower-case). – Robert Andrews May 11 '20 at 06:55
  • 2
    @RobertAndrews you can use strcasecmp instead of strcmp: https://www.php.net/manual/en/function.strcasecmp.php – cambraca May 11 '20 at 22:30
  • 2
    `return $a->count <=> $b->count;` is recommended when comparing numbers – Deepak Kamat Jul 06 '20 at 15:45
524

Heres a nicer way using closures

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

Please note this is not in PHP's documentation but if you using 5.3+ closures are supported where callable arguments can be provided.

Scott Quinlan
  • 5,276
  • 1
  • 13
  • 3
  • 19
    I love this one better than the accepted answer since we can quickly define the compare function and can use in a class – Nam G VU May 01 '12 at 18:43
  • This worked for me, however the accepted answer did not. The class would not recognize the callback function by name. I am not sure why. Thanks – usumoio Sep 05 '12 at 15:25
  • 1
    @IamJohnGalt - Maybe this is relevant: look [this comment on php.net](http://www.php.net/manual/en/function.uasort.php#100485) – Bruno Belotti May 28 '13 at 22:39
  • This can also be used for an array of arrays FWIW (using `return strcmp($a['name'], $b['name']);`) :) – neemzy Nov 07 '13 at 11:33
  • Much better solution. The use of closures makes it much more obvious. – Richard Clayton Jan 12 '15 at 17:08
  • This is better for 5.3+. I don't want to define a function, rather use a closure. – Buttle Butkus Mar 01 '15 at 03:35
  • 12
    If you want to preserve the array keys use `uasort()` – gillytech May 01 '15 at 00:36
  • One of the best answers I've ever come across on StackOverflow, I wish I could up it more than once. Should be the accepted answer! – H2ONOCK May 12 '15 at 19:41
  • 10
    For sort desc, `-1 * strcmp($a->name, $b->name);` – Wallace Vizerra Jul 14 '15 at 13:59
  • 22
    No need to multiply to sort desc. Just swap args: `strcmp($b->name, $a->name)` – zxcat Mar 19 '16 at 00:53
  • 2
    `strnatcmp` for ordering like a person would – John Erck May 05 '16 at 18:22
  • 3
    You may encounter a situation, like me, where the accepted answer is preferable to this one. For instance, you may have a parent and a child class. The child class overrides a function that uses `usort` but the comparison function is the same. Using this you'd need to duplicate the code for the closure instead of doing a call to a `protected static` method you would need to define only once in the parent class. – Pere Sep 28 '16 at 15:12
83

If you want to sort integer values:

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

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

UPDATED with the string don't forget to convert to the same register (upper or lower)

// 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
  • 1,614
  • 15
  • 19
  • For me, $first->number didn't work. I needed to use $first["number"] instead. – Androidz Jun 19 '21 at 15:45
  • Instead of using `strtolower()`, you could compare strings and ignore case with the PHP native `strcasecmp()` function (Read [PHP: strcasecmp - Manual](https://www.php.net/manual/fr/function.strcasecmp.php)) – Zoup Aug 16 '21 at 15:09
46

if you're using php oop you might need to change to:

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')); 
Andy
  • 17,423
  • 9
  • 52
  • 69
Doron Segal
  • 2,242
  • 23
  • 22
34
usort($array, 'my_sort_function');

var_dump($array);

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

The same code will be with the count field.

More details about usort: http://ru2.php.net/usort

Btw, where did you get that array from? I hope that not from database?

zerkms
  • 249,484
  • 69
  • 436
  • 539
  • 1
    Actually `$result` will contain `TRUE` if it's successful, and your comparison should be `$a->name > $b->name`. :) – cambraca Nov 26 '10 at 03:56
  • 2
    @cambraca: oh, forgot it accepts array by reference. Btw, OP did not said which order he need to sort collection. – zerkms Nov 26 '10 at 03:57
  • 1
    well yes, it's a database :) actually from a function that gets the data from the database – Alex Nov 26 '10 at 04:01
  • 3
    @Alex: why don't you sort it in database then? `ORDER BY count` – zerkms Nov 26 '10 at 04:02
  • 1
    it's more complicated, because that's a stadard function part of wordpress, and as I'm writing a plugin, I can't change wp files. I tried your example using create_function (because I'm using it inside a class and I don't know how to pass the function name to usort): `create_function('$a,$b', "return $a->count < $b->count;")` but I can't make it work :( I get a few notices and warning that usort expects parameter 2 to be a valid callback – Alex Nov 26 '10 at 04:11
  • 1
    ok, i managed to do it by adapting example 3 on the usort php page. thanks :) – Alex Nov 26 '10 at 04:20
  • 1
    @Alex: I had the same issue, and it was caused because I was calling a method within the same class. I resolved it by using this format: usort($array, array($this,"array_sort_function")); – Eric Di Bari Apr 28 '12 at 21:21
  • 1
    @zerkms: Sometimes (if you know that the size of returned set isn't huge) it's better to perform in-memory sorting instead of DB-sorting. E.g. if you have a few million rows in table in Oracle (without index) and you need to take max. 10 and sort them. – Piohen Oct 30 '13 at 13:43
  • 1
    @Piohen: "you have a few million rows in table in Oracle (without index) and you need to take max. 10 and sort them" --- do you realize that without index "to take max. 10" task - will cause the full table scan and in-memory sorting or "few million rows"? – zerkms Oct 30 '13 at 21:18
  • 1
    @zerkms: There's a misunderstanding, I think. I just wanted to point out that blindly using "order by" in SQL query is not a good idea. Sometimes it's better to perform in-memory sort in application: PHP, Java, etc. Even JavaScript sometimes. So IMHO "always use ORDER BY, never sort in application" is bad advice. – Piohen Oct 31 '13 at 09:36
  • 1
    @Piohen: well, I don't agree. If only you could provide a *real* example when that "sometimes" makes sense. – zerkms Oct 31 '13 at 09:41
  • 1
    @zerkms: I just did a few comments before ;-) If you need to truly experience it, why don't you replicate it yourself and measure times? Taking from DB 10 sorted rows out of millions must be slower than taking the same 10 unsorted rows out of millions. Some RDBMses ALWAYS use disk space and temp tables for sorting, that's why sorting these 10 rows (even using bubble sort) will be faster in your app using in-memory sorting. And if you sort them in JavaScript, you don't take the sorting load at all. RDMS isn't always faster than your app. EOT for me. – Piohen Oct 31 '13 at 09:58
  • 1
    @Piohen: "Taking from DB 10 sorted rows out of millions must be slower than taking the same 10 unsorted rows out of millions." --- provide context, when you need to take not 10 rows in a particular order (like last 10 rows) but "some rows" out of 10M. I cannot think of a real task for that. Just for information: I have worked with databases of a size about 1Tb and I cannot understand why you're scared of using `ORDER BY` with just 10M rows. – zerkms Oct 31 '13 at 10:36
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/40306/discussion-between-zerkms-and-piohen) – zerkms Oct 31 '13 at 10:36
  • "Some RDBMses ALWAYS use disk space and temp tables for sorting" --- I'm not aware of them. The DBMSes I ever worked with more or less professionally (mysql, postgresql, sql server, oracle) use indexes for sorting, if you index properly and write nice queries. – zerkms Oct 31 '13 at 10:40
11

If everything fails here is another 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.
  • 5,060
  • 2
  • 46
  • 47
10

You can use this function (works in PHP Version >= 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;

        });
    }
}

Example:

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
  • 153
  • 1
  • 7
  • I used `$a->{$key}` and `$b->{$key}` rather than `$a[$key]` and `$b[$key]` as we are, strictly speaking, dealing with properties rather than array members, but this was still the answer I was looking for. – SteJ Nov 19 '16 at 15:19
  • Please implement @SteJ's suggestion in the example code as the solution you give only works for simple objects but with SteJ's fix it works for all arrays of objects I have tried it on – user2993145 Jun 08 '17 at 15:22
9

To sort on one column of values, a combination of array_column() and array_multisort() is one sensible way. Demo

array_multisort(array_column($array, 'count'), $array);

Or only call upon usort() with the spaceship operator to perform less iterating in this scenario. Demo

usort($array, fn($a, $b) => $a->count <=> $b->count);

Notice that although the count values are cast as string type values in the input array, both sorting functions will correctly sort the values numerically instead of alphabetizing them (erroneously putting 23420 before 420). This is a reliable default feature.

Even if you are variably declaring the column to sort on, both approaches allow the variable to be used without any addition techniques.

Multisort Demo with variable

$property = 'count';
array_multisort(array_column($array, $property), $array);

Usort Demo with variable

$property = 'count';
usort($array, fn($a, $b) => $a->$property <=> $b->$property);

Both native sorting functions modify by reference, so do not try to access the sorted array by their return value.

array_multisort()'s default sorting direction is ascending, so it is of no benefit to explicitly use the SORT_ASC between the two array parameters. If descending sorting is desired, write SORT_DESC between the two arrays (as the second parameter).

usort() will sort ascending when the custom function body puts $a data on the left side of the spaceship operator and $b data on the right side. For sorting in a descending direction, just write $b data on the left and $a data on the right.

Both approaches are capable of receiving multiple sorting rules, but because this question only asks to sort on a single column, that guidance is inappropriate here.

It will be less efficient to call a function (like strcmp()) on every iteration while sorting. This is no longer best practice. Neither is using a two-way comparison (like > or <) to return a boolean outcome. A three-way comparison is expected from usort().

For sorting data with multiple rules/columns/properties, this answer gives good guidance.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
6

You can use usort, like this:

usort($array,function($first,$second){
    return strcmp($first->name, $second->name);
});
Roman Yakoviv
  • 1,614
  • 15
  • 19
Luca C.
  • 11,714
  • 1
  • 86
  • 77
5

if you want to sort dates

   usort($threads,function($first,$second){
        return strtotime($first->dateandtime) < strtotime($second->dateandtime);
    });
Nicolas Giszpenc
  • 677
  • 6
  • 11
  • This is an answer to a different question. The original question is not sorting date expressions. Stack Overflow has other sorting pages dedicated to this task. – mickmackusa Jul 01 '22 at 14:33
4

Downside of all answers here is that they use static field names, so I wrote an adjusted version in OOP style. Assumed you are using getter methods you could directly use this Class and use the field name as parameter. Probably someone find it useful.

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
  • 8,923
  • 1
  • 29
  • 47
3

If you need local based string comparison, you can use strcoll instead of strcmp.

Remeber to first use setlocale with LC_COLLATE to set locale information if needed.

  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,245
  • 4
  • 33
  • 37
3

A simple alternative that allows you to determine dynamically the field on which the sorting is based:

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

This is based on the Closure class, which allows anonymous functions. It is available since PHP 5.3.

clami219
  • 2,958
  • 1
  • 31
  • 45
2

If you are using this inside Codeigniter, you can use the methods:

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

@rmooney thank you for the suggestion. It really helps me.

Muhammad Raheel
  • 19,823
  • 7
  • 67
  • 103
2

Thanks for the inspirations, I also had to add an external $translator parameter

usort($listable_products, function($a, $b) {
    global $translator;
    return strcmp($a->getFullTitle($translator), $b->getFullTitle($translator));
});
michalzuber
  • 5,079
  • 2
  • 28
  • 29
1

If you need to sort by only one field, then usort is a good choice. However, the solution quickly becomes messy if you need to sort by multiple fields. In this case, YaLinqo library* can be used, which implements SQL-like query syntax for arrays and objects. It has a pretty syntax for all cases:

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

Here, '$v->count' is a shorthand for function ($v) { return $v->count; } (either can be used). These method chains return iterators, but you can get arrays by adding ->toArray() in the end if you need it.

* developed by me

Athari
  • 33,702
  • 16
  • 105
  • 146
1

You can use sorted function from 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
  • 4,689
  • 1
  • 26
  • 25
  • Please explain why the OP would need a whole additional library to provide a facility seemingly resolved by built in functions – ggdx Aug 10 '18 at 11:36
1

This is what I have for a utility class

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

Call it:

Util::sortArrayByName($array, "array_property_name");
Demodave
  • 6,242
  • 6
  • 43
  • 58
1

You can use usort like this

If you want to sort by number:

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

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

usort($a, "cmp");

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

See more: https://www.php.net/manual/en/function.usort.php

Alex
  • 3,646
  • 1
  • 28
  • 25
  • This unexplained answer is completely ignoring the fact that the original question is sorting an array of objects. – mickmackusa Jul 01 '22 at 14:39
1
$array[0] = array('key_a' => 'z', 'key_b' => 'c');
$array[1] = array('key_a' => 'x', 'key_b' => 'b');
$array[2] = array('key_a' => 'y', 'key_b' => 'a');

function build_sorter($key) {
    return function ($a, $b) use ($key) {
        return strnatcmp($a[$key], $b[$key]);
    };
}

usort($array, build_sorter('key_b'));
C Williams
  • 850
  • 12
  • 19
0

reference answer of Demodave to eating multi key

 function array_sort_by(array $arr, $keys){

    if(!is_array($keys))
        $keyList = explode(',', $keys);
    $keyList = array_keys(array_flip($keyList)); // array_unique 
    $keyList = array_reverse($keyList);

    $result = &$arr;
    foreach ($keyList as $key) {
        if(array_key_exists($key, $arr))
            $result = usort($result, function($a, $b) use ($key) { return strcmp($a->{$key}, $b->{$key}); });
    }
    return $result;
}
0

use this....

$array_list = [
    "Apple" => 2,
    "Pear" => 1,
    "Orange" => 5,
    "Lemon" => 1,
    "Strawberry" => 2,
    "Banana" => 3
];

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

$ao = new ArrayObject($object);
$ao->uasort('cmp');
print_r(json_encode($ao));

Bye!!!!

jim smith
  • 2,394
  • 5
  • 29
  • 35
-2

For my part, here is how I proceeded to sort an array of objects by object fields:

Code: (Demo) -- sorts by last_name ASC, then first_name ASC

<?php

$array = array(
    (object)array(
        'first_name' => 'Léa',
        'last_name' => 'Weber',
    ),
    (object)array(
        'first_name' => 'Alexandre',
        'last_name' => 'Dupont',
    ),
    (object)array(
        'first_name' => 'Léa',
        'last_name' => 'Zotal',
    ),
    (object)array(
        'first_name' => 'Jérémie',
        'last_name' => 'Hoffmann',
    )
);

usort($array, function($a, $b) {
    return [$a->last_name, $a->first_name]
           <=>
           [$b->last_name, $b->first_name];
});


var_export($array);

Outpout:

array (
  0 => 
  (object) array(
     'first_name' => 'Alexandre',
     'last_name' => 'Dupont',
  ),
  1 => 
  (object) array(
     'first_name' => 'Jérémie',
     'last_name' => 'Hoffmann',
  ),
  2 => 
  (object) array(
     'first_name' => 'Léa',
     'last_name' => 'Weber',
  ),
  3 => 
  (object) array(
     'first_name' => 'Léa',
     'last_name' => 'Zotal',
  ),
)

Arrow syntax with PHP7.4 and higher. Makes sorting by multiple columns SUPER easy with the spaceship operator (<=>) aka the "Combined Comparison Operator" or "Three-way Comparison Operator".

Resource: https://wiki.php.net/rfc/combined-comparison-operator https://stackoverflow.com/a/54647220/18090932

Elell
  • 15
  • 2