PHPのお勉強!

PHP TOP

usort

(PHP 4, PHP 5, PHP 7, PHP 8)

usortユーザー定義の比較関数を使用して、配列を値でソートする

説明

usort(array &$array, callable $callback): true

順序を決めるユーザー定義の比較関数により、 array をその値でソートします。

注意:

比較結果が等しくなる二つの要素があった場合、それらの並び順は保持されます。PHP 8.0.0 より前のバージョンでは、ソートした配列におけるそれらの並び順は不定でした。

注意: この関数は、 array パラメータの要素に対して新しいキーを割り当てます。 その際、単純にキーを並べ替える代わりに、 すでに割り当てられている既存のキーを削除してしまいます。

パラメータ

array

入力の配列。

callback

比較関数は、最初の引数と二番目の引数の比較結果を返します。最初の引数のほうが二番目の引数より大きい場合は正の整数を、二番目の引数と等しい場合はゼロを、そして二番目の引数より小さい場合は負の整数を返す必要があります。

callback(mixed $a, mixed $b): int
警告

float のような 非整数 を比較関数が返すと、その返り値を内部的に int にキャストして使います。 つまり、0.990.1 といった値は整数値 0 にキャストされ、 値が等しいとみなされます。

戻り値

常に true を返します。

変更履歴

バージョン 説明
8.2.0 戻り値の型が、true になりました。これより前のバージョンでは、bool でした。
8.0.0 callback がリファレンス渡しされる引数を期待している場合、 この関数は E_WARNING を発生させるようになりました。

例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

内部的な比較をさらにシンプルにするために、 宇宙船演算子を使うこともできます。

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

注意:

もちろん、このような簡単な例では rsort() 関数の方がより適当です。

例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";
}
?>

多次元配列をソートする際には、$a$b は配列の最初のインデックスへの参照を保持しています。

上の例の出力は以下となります。

$fruits[0]: apples
$fruits[1]: grapes
$fruits[2]: lemons

例3 usort() でオブジェクトのメンバ関数を使用する例

<?php
class TestObj {
private
string $name;

function
__construct($name)
{
$this->name = $name;
}

/* これは、比較用のstaticメソッドです */
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 を last name で比較し、 それが一致するものについては first name で比較します。

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

参考

  • uasort() - ユーザー定義の比較関数で配列をソートし、連想インデックスを保持する
  • uksort() - ユーザー定義の比較関数を用いて、キーで配列をソートする
  • 配列ソート関数の比較
add a note

User Contributed Notes 11 notes

up
21
Hayley Watson
11 years ago
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".
up
12
luke dot semerau at gmail dot com
15 years ago
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)
up
14
mkr at binarywerks dot dk
22 years ago
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);
?>
up
8
derek at luddite dot net
24 years ago
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 delimeter
up
5
sydney at totoche dot org
18 years ago
Instead 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.
up
2
gus dot antoniassi at gmail dot com
5 years ago
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.
up
4
inigo dot grimbergen at gmail dot com
7 years ago
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
null
up
3
andi_mclean at ntlworld dot com
12 years ago
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);
}
?>
up
1
chris at candm dot org dot uk
5 years ago
In 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.
up
2
bo at erichsen dot com
23 years ago
when using usort to refer to a function inside a class i have succesfully used:

<?php usort($myarray,array($this,"cmp")); ?>
up
1
rh at 20i dot com
4 months ago
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
)
```