Alex
Alex

Reputation: 68396

Sort array of objects by one property

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
        )

   ....

Upvotes: 689

Views: 563432

Answers (23)

cambraca
cambraca

Reputation: 27839

Use usort to customize the comparison function. 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.

Upvotes: 976

Scott Quinlan
Scott Quinlan

Reputation: 5336

Here's 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.

Upvotes: 530

mickmackusa
mickmackusa

Reputation: 47844

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.

Upvotes: 14

Elell
Elell

Reputation: 15

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

Upvotes: -2

juliosantos73
juliosantos73

Reputation: 375

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

Upvotes: 0

C Williams
C Williams

Reputation: 859

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

Upvotes: 1

Chaos Fractal
Chaos Fractal

Reputation: 75

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

Upvotes: 0

Alex
Alex

Reputation: 4266

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

Upvotes: 1

Demodave
Demodave

Reputation: 6632

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

Upvotes: 1

clami219
clami219

Reputation: 3038

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.

Upvotes: 3

Nicolas Giszpenc
Nicolas Giszpenc

Reputation: 703

if you want to sort dates

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

Upvotes: 5

Roman Yakoviv
Roman Yakoviv

Reputation: 1724

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

Upvotes: 87

Adrian P.
Adrian P.

Reputation: 5228

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;

Upvotes: 11

Luca C.
Luca C.

Reputation: 12564

You can use usort, like this:

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

Upvotes: 6

PoengAlex
PoengAlex

Reputation: 153

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)

Upvotes: 10

michalzuber
michalzuber

Reputation: 5215

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

Upvotes: 2

Ihor Burlachenko
Ihor Burlachenko

Reputation: 4905

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

Upvotes: 1

oshell
oshell

Reputation: 9103

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

Upvotes: 4

Athari
Athari

Reputation: 34275

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

Upvotes: 1

PHP Developer
PHP Developer

Reputation: 81

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.

Upvotes: 2

Doron Segal
Doron Segal

Reputation: 2260

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

Upvotes: 46

Wilq
Wilq

Reputation: 2272

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

Upvotes: 3

zerkms
zerkms

Reputation: 254886

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: https://www.php.net/usort

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

Upvotes: 35

Related Questions