Reputation: 68396
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
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
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
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.
$property = 'count';
array_multisort(array_column($array, $property), $array);
$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
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
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
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
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
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
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
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
Reputation: 703
if you want to sort dates
usort($threads,function($first,$second){
return strtotime($first->dateandtime) < strtotime($second->dateandtime);
});
Upvotes: 5
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
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
Reputation: 12564
You can use usort
, like this:
usort($array,function($first,$second){
return strcmp($first->name, $second->name);
});
Upvotes: 6
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
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
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
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
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
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
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
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
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