(PHP 4, PHP 5, PHP 7, PHP 8)
usort — Сортирует массив по значениям через пользовательскую функцию сравнения элементов
Функция сортирует массив array на месте по значениям
путем обработки элементов массива пользовательской функцией сравнения для определения порядка.
Замечание: Функция сохраняет первоначальный порядок элементов, если при сравнении значения двух элементов равны. До PHP 8.0.0 взаимный порядок одинаковых по значению элементов в отсортированном массиве оставался неопределённым.
Замечание: Функция присваивает новые ключи элементам массива, который передали в аргументе
array. Функция удалит, а не просто переупорядочит ключи входного массива.
arrayВходной массив.
callback
Функция возвращает логическое значение true .
| Версия | Описание |
|---|---|
| 8.2.0 |
Вместо общего логического типа bool теперь возвращается конкретный тип true .
|
| 8.0.0 |
Функция теперь выдаст ошибку уровня E_WARNING ,
если параметр callback-функции ожидает
передачу значения по ссылке.
|
Пример #1 Пример сортировки массива функцией usort()
<?php
function cmp($a, $b)
{
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
$a = array(3, 2, 5, 6, 1);
usort($a, "cmp");
foreach ($a as $key => $value) {
echo "$key: $value\n";
}
?>Результат выполнения приведённого примера:
0: 1 1: 2 2: 3 3: 5 4: 6
Для ещё большего упрощения внутреннего сравнения указывают оператор «космический корабль» (англ. spaceship).
<?php
function cmp($a, $b)
{
return $a <=> $b;
}
$a = array(3, 2, 5, 6, 1);
usort($a, "cmp");
foreach ($a as $key => $value) {
echo "$key: $value\n";
}
?>Замечание:
Ясно, что в этом тривиальном случае функция sort() была бы уместнее.
Пример #2 Пример сортировки функцией usort() многомерного массива
<?php
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");
foreach ($fruits as $key => $value) {
echo "\$fruits[$key]: " . $value["fruit"] . "\n";
}
?>Результат выполнения приведённого примера:
$fruits[0]: apples $fruits[1]: grapes $fruits[2]: lemons
При сортировке многомерного массива переменные $a и $b содержат ссылки на первые два индекса массива.
Пример #3 Пример сортировки массива функцией usort() через функцию-член объекта
<?php
class TestObj
{
public string $name;
function __construct($name)
{
$this->name = $name;
}
/* Статическая функция сравнения: */
static function cmp_obj($a, $b)
{
return strtolower($a->name) <=> strtolower($b->name);
}
}
$a[] = new TestObj("c");
$a[] = new TestObj("b");
$a[] = new TestObj("d");
usort($a, [TestObj::class, "cmp_obj"]);
foreach ($a as $item) {
echo $item->name . "\n";
}
?>Результат выполнения приведённого примера:
b c d
Пример #4 Пример вызова функции usort() с замыканием для сортировки многомерного массива
<?php
$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'));
foreach ($array as $item) {
echo $item['key_a'] . ', ' . $item['key_b'] . "\n";
}
?>Результат выполнения приведённого примера:
y, a x, b z, c
Пример #5 Пример работы функции usort() с оператором «космический корабль»
Оператор «космический корабль» упрощает сравнение
составных значений по набору критериев. В следующем примере
функция сортирует массив $people по фамилии, а затем по имени, если фамилии совпадают.
<?php
$people[0] = ['first' => 'Adam', 'last' => 'West'];
$people[1] = ['first' => 'Alec', 'last' => 'Baldwin'];
$people[2] = ['first' => 'Adam', 'last' => 'Baldwin'];
function sorter(array $a, array $b)
{
return [$a['last'], $a['first']] <=> [$b['last'], $b['first']];
}
usort($people, 'sorter');
foreach ($people as $person) {
print $person['last'] . ', ' . $person['first'] . PHP_EOL;
}
?>Результат выполнения приведённого примера:
Baldwin, Adam Baldwin, Alec West, Adam
As the documentation says, the comparison function needs to return an integer that is either "less than, equal to, or greater than zero". There is no requirement to restrict the value returned to -1, 0, 1.
<?php
usort($array, function($a, $b) {
if($a->integer_property > $b->integer_property) {
return 1;
}
elseif($a->integer_property < $b->integer_property) {
return -1;
}
else {
return 0;
}
});
?>
can be simplified to
<?php
usort($array, function($a, $b) {
return $a->integer_property - $b->integer_property;
});
?>
This of course applies to any comparison function that calculates an integer "score" for each of its arguments to decide which is "greater".If you want to sort an array according to another array acting as a priority list, you can use this function.
<?php
function listcmp($a, $b)
{
global $order;
foreach($order as $key => $value)
{
if($a==$value)
{
return 0;
break;
}
if($b==$value)
{
return 1;
break;
}
}
}
$order[0] = "first";
$order[1] = "second";
$order[2] = "third";
$array[0] = "second";
$array[1] = "first";
$array[2] = "third";
$array[3] = "fourth";
$array[4] = "second";
$array[5] = "first";
$array[6] = "second";
usort($array, "listcmp");
print_r($array);
?>If you need to use usort with a key in the calling method, I wrote this as a utility:
<?php
function usort_comparison($obj, $method, $key) {
$usorter = &new Usort($obj, $method, $key);
return array($usorter, "sort");
}
class Usort {
function __construct($obj, $method, $key) {
$this->obj = $obj;
$this->method = $method;
$this->key = $key;
}
function sort($a, $b) {
return call_user_func_array(array($this->obj, $this->method), array($a, $b, $this->key));
}
}
?>
<?php
require_once("util/usort.php");
class Foo {
$items = array(FooBar(13), FooBar(2));
public function sorter() {
usort($this-items, usort_comparison("Foo", "_cmp", "item"));
}
public static function _cmp($a, $b, $key) {
return strcasecmp($a->$key, $b->$key);
}
}
class FooBar {
public $item;
function __construct($val) {
$this->item = $val;
}
}
?>
~ simple example... but in the way I need to use it was the key was used in a switch statement to choose the different member of the object to compare against dynamically (as in, sort by x or y or z)Needed a date sort and I didn't know if one was available so I wrote one. Maybe it'll help someone:
<?php
function DateSort($a,$b,$d="-") {
if ($a == $b) {
return 0;
} else { //Convert into dates and compare
list($am,$ad,$ay)=split($d,$a);
list($bm,$bd,$by)=split($d,$b);
if (mktime(0,0,0,$am,$ad,$ay) < mktime(0,0,0,$bm,$bd,$by)) {
return -1;
} else {
return 1;
}
}
}
?>
$d is the delimeterInstead of doing :
<?php $strc = strcmp( strtolower($a[$f]), strtolower($b[$f]) ); ?>
you could do this :
<?php $strc = strcasecmp( $a[$f], $b[$f] ); ?>
which is more efficient and is does case insensitive comparison according to the current locale.This is a simple way to sort based on a "priority list":
<?php
$order = [1,3,0,2];
$arr = [
[ 'id' => 0 ],
[ 'id' => 1 ],
[ 'id' => 2 ],
[ 'id' => 3 ],
];
uasort(
$arr,
function ($a, $b) use ($order) {
return array_search($a['id'], $order) <=> array_search($b['id'], $order);
}
);
print_r($arr);
?>
This will return:
Array
(
[1] => Array
(
[id] => 1
)
[3] => Array
(
[id] => 3
)
[0] => Array
(
[id] => 0
)
[2] => Array
(
[id] => 2
)
)
Note that if you have a value in $arr that is not on the $order list, you will need additional checks since the array_search function returns FALSE for undefined indexes.to sort with numeric and empty values and have the smallest on top:
<?php
usort($list, function($a, $b) {
if( $a == null && $b != null ) return 1;
if( $a != null && $b == null ) return -1;
return $a > $b ? 1 : -1;
});
?>
returns
1
2
3
null
null
nullIn case anyone is interested, comparative timings over 100000000 runs
Based on comparing integers (500 and 501)
Spaceship:4
()?: operator:10
Subtraction:2
Based on comparing floats (500.1 and 501.3) (caveats noted)
Spaceship:5
()?: operator:9
Subtraction:3
Based on comparing strings ("five" and "four")
Spaceship:7
()?: operator:17
(Subtraction obviously not available)
Note: a dummy run was done with an empty loop and the elapsed time for this was subtracted from each of the above times so that they reflect ONLY the time to do the comparisons. As for significance. unless you are doing very large numbers of comparisons where spaceships are the order of the day, the difference is insignificant.when using usort to refer to a function inside a class i have succesfully used:
<?php usort($myarray,array($this,"cmp")); ?>I needed a sort method that would sort strings but take note of any numbers and would compare them as number. I also want to ignore any non alphanumerical characters.
Eg.
Slot 1 Example
Slot 10 Example
Slot 2 Example
Should infact be
Slot 1 Example
Slot 2 Example
Slot 10 Example
<?php
function sort_with_numbers($a , $b) {
$a = explode(' ',$a);
$b = explode(' ',$b);
$size = min(count($a), count($b));
for($index =0; $index < $size; ++$index) {
$a1 = ereg_replace("[^A-Za-z0-9]", "",$a[$index]);
$b1 = ereg_replace("[^A-Za-z0-9]", "",$b[$index]);
$equal = 0;
if (is_numeric($a1) && is_numeric($b1)) {
$equal = $a1 - $b1;
} else {
$equal = strcasecmp($a1,$b1);
}
if ($equal < 0) {
return -1;
}
if ($equal > 0) {
return 1;
}
}
return count($a) - count($b);
}
?>In PHP8, you can write comparator like this for handle multiple column keys sort
<?php
class Comparator {
public static function default_comp($a, $b) {
return $a <=> $b;
}
private $comp;
public function __construct($comp = [self::class, 'default_comp']) {
$this->comp = $comp;
}
public function reverse() {
return new self(fn($a, $b) => ($this->comp)($b, $a));
}
public function column($key) {
return new self(fn($a, $b) => ($this->comp)($a[$key], $b[$key]));
}
public static function chain_impl($a, $b, ...$comp_list) {
foreach ($comp_list as $comp) {
if ($res = $comp($a, $b)) {
return $res;
}
}
return 0;
}
public function chain(...$comp_list) {
return new self(fn($a, $b) => self::chain_impl($a, $b, $this->comp, ...$comp_list));
}
public function __invoke($a, $b) {
return ($this->comp)($a, $b);
}
}
$students = array(
array('name' => 'Adam', 'score' => 90, 'grade' => 3),
array('name' => 'Bob', 'score' => 95, 'grade' => 2),
array('name' => 'Steve', 'score' => 90, 'grade' => 1),
);
// score descending
$grade_comp = (new Comparator())->column('score')->reverse();
// name ascending
$name_comp = (new Comparator('strcmp'))->column('name');
// $students = array(
// array('name' => 'Bob', 'score' => 95, 'grade' => 2),
// array('name' => 'Adam', 'score' => 90, 'grade' => 3),
// array('name' => 'Steve', 'score' => 90, 'grade' => 1),
// );
usort($students, $grade_comp->chain($name_comp));
?>A sort function to sort elements by a reference order.
function sort_by_reference(array $array_to_sort, array $reference_array): array {
usort($array_to_sort, function($a, $b) use ($reference_array) {
$pos_a = array_search($a, $reference_array);
$pos_b = array_search($b, $reference_array);
return $pos_a - $pos_b;
});
return $array_to_sort;
}
// Example usage
$reference_array = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"];
$array_to_sort = ["three", "one", "seven", "four", "ten"];
$sorted_array = sort_by_reference($array_to_sort, $reference_array);
// Print the result to verify the sorting
print_r($sorted_array);
```
Array
(
[0] => one
[1] => three
[2] => four
[3] => seven
[4] => ten
)
```