はじめに
これまでの記事では、キーを保持したまま値をソートする uasort() と、キー自体をソートする uksort() を紹介してきました。今回はその原点ともいえる usort() を解説します。
usort() は、配列の値をユーザー定義の比較関数でソートする関数です。名前の “u” は “user-defined”(ユーザー定義)を意味します。uasort() との最大の違いは、ソート後に配列のキーが保持されず、0からの連番インデックスに振り直されるという点です。オブジェクトの配列を特定のプロパティで並び替えたり、複数条件で優先順位を決めたりする場面で非常によく使われる、実務上の重要度が高い関数です。本記事では基本的な使い方から実践的な活用例まで詳しく解説します。
関数概要
| 項目 | 内容 |
|---|---|
| 関数名 | usort() |
| 所属拡張 | コア関数(標準で常に利用可能) |
| シグネチャ | usort(array &$array, callable $callback): bool |
| 引数1 | $array — ソート対象の配列(参照渡し) |
| 引数2 | $callback — 2つの値を比較するコールバック関数 |
| 戻り値 | 成功時に true |
| 対応バージョン | PHP 4以降 |
| キーの扱い | 保持されない(0からの連番に振り直される) |
| 類似関数 | uasort()(キーを保持)、uksort()(キーをソート) |
ソートの流れ(イメージ図)
ソート前の配列(連想配列でもOK)
['b' => 3, 'a' => 1, 'c' => 2]
│
▼
usort($array, fn($a, $b) => $a <=> $b)
│
┌──────────────┴──────────────┐
│ 値を比較関数で比較して並び替える │
│ ※ ソート後、元のキーは失われ、 │
│ 0からの連番に振り直される │
└──────────────┬──────────────┘
▼
ソート後の配列(インデックスは0, 1, 2...に)
[0 => 1, 1 => 2, 2 => 3]
ポイントは、ソートの過程で元のキー情報が完全に失われるという点です。これは uasort() との明確な違いであり、「配列のキーに意味がない(単なる連番でよい)場合」に usort() を使い、「キーに意味があり保持したい場合」に uasort() を使う、という使い分けが基本になります。
実践サンプル7選
例1:基本的な使い方
<?php
class BasicUsortDemo
{
public function sortAscending(array $numbers): array
{
// 昇順に並び替える単純な比較関数
usort($numbers, fn ($a, $b) => $a <=> $b);
return $numbers;
}
}
$demo = new BasicUsortDemo();
print_r($demo->sortAscending([5, 3, 8, 1, 9]));
// [0 => 1, 1 => 3, 2 => 5, 3 => 8, 4 => 9]
例2:オブジェクトの配列を複数プロパティでソートするクラス
<?php
class Product
{
public function __construct(
public string $name,
public float $price,
public int $stock
) {
}
}
class ProductSorter
{
/**
* 価格の安い順、価格が同じ場合は在庫数の多い順にソートする
*/
public function sortByPriceThenStock(array $products): array
{
usort($products, function (Product $a, Product $b) {
$priceDiff = $a->price <=> $b->price;
if ($priceDiff !== 0) {
return $priceDiff;
}
return $b->stock <=> $a->stock;
});
return $products;
}
}
$sorter = new ProductSorter();
$products = [
new Product('マグカップ', 1200, 10),
new Product('ノート', 300, 50),
new Product('ペン', 300, 20),
];
foreach ($sorter->sortByPriceThenStock($products) as $product) {
echo "{$product->name}: {$product->price}円 (在庫{$product->stock})" . PHP_EOL;
}
// ノート(在庫50) → ペン(在庫20) → マグカップ
例3:日付文字列で時系列順にソートする
<?php
class EventChronologicalSorter
{
/**
* イベントデータの配列を開催日順にソートする
*/
public function sort(array $events): array
{
usort($events, function (array $a, array $b) {
return strtotime($a['date']) <=> strtotime($b['date']);
});
return $events;
}
}
$sorter = new EventChronologicalSorter();
$events = [
['name' => '技術セミナー', 'date' => '2026-09-10'],
['name' => '新年会', 'date' => '2026-01-15'],
['name' => '夏祭り', 'date' => '2026-08-01'],
];
print_r($sorter->sort($events));
例4:usortとuasortの挙動比較(キーの扱いの違い)
<?php
class KeyBehaviorComparator
{
/**
* 同じ連想配列に対してusort()とuasort()を適用し、
* キーの扱いの違いを確認する
*/
public function compare(array $data): array
{
$forUsort = $data;
$forUasort = $data;
usort($forUsort, fn ($a, $b) => $a <=> $b);
uasort($forUasort, fn ($a, $b) => $a <=> $b);
return [
'usort' => $forUsort, // キーは0からの連番に
'uasort' => $forUasort, // 元のキーが保持される
];
}
}
$comparator = new KeyBehaviorComparator();
print_r($comparator->compare(['x' => 3, 'y' => 1, 'z' => 2]));
例5:日本語文字列を読み仮名(ふりがな)順にソートする
<?php
class KanaSorter
{
/**
* 名前と読み仮名のペアを持つ配列を、
* 読み仮名でソートする
*/
public function sortByKana(array $people): array
{
usort($people, function (array $a, array $b) {
return strcmp($a['kana'], $b['kana']);
});
return $people;
}
}
$sorter = new KanaSorter();
$people = [
['name' => '田中太郎', 'kana' => 'たなかたろう'],
['name' => '佐藤花子', 'kana' => 'さとうはなこ'],
['name' => '鈴木一郎', 'kana' => 'すずきいちろう'],
];
print_r($sorter->sortByKana($people));
例6:ランキング形式のデータをスコアの高い順に並び替える
<?php
class ScoreRankingSorter
{
/**
* スコアの高い順(降順)に並び替える
*/
public function sortDescending(array $scores): array
{
// 比較関数の引数順を入れ替えることで降順ソートを実現する
usort($scores, fn ($a, $b) => $b['score'] <=> $a['score']);
return $scores;
}
}
$sorter = new ScoreRankingSorter();
$scores = [
['player' => 'A', 'score' => 8200],
['player' => 'B', 'score' => 9500],
['player' => 'C', 'score' => 7600],
];
print_r($sorter->sortDescending($scores));
// B(9500) → A(8200) → C(7600)
例7:ファーストクラスコーラブル構文を使ったスマートなソート実装(PHP 8.1以降)
<?php
class Item
{
public function __construct(public string $name, public int $weight)
{
}
public static function compareByWeight(Item $a, Item $b): int
{
return $a->weight <=> $b->weight;
}
}
class ItemSorter
{
public function sortByWeight(array $items): array
{
// PHP 8.1以降のファーストクラスコーラブル構文で
// 静的メソッドをそのままコールバックとして渡せる
usort($items, Item::compareByWeight(...));
return $items;
}
}
$sorter = new ItemSorter();
$items = [
new Item('荷物A', 12),
new Item('荷物B', 5),
new Item('荷物C', 8),
];
foreach ($sorter->sortByWeight($items) as $item) {
echo "{$item->name}: {$item->weight}kg" . PHP_EOL;
}
// 荷物B(5kg) → 荷物C(8kg) → 荷物A(12kg)
関連関数との比較
| 関数 | 役割 | usortとの違い |
|---|---|---|
usort() | 値をユーザー定義の比較関数でソート | 本記事の対象。キーは保持されず連番に振り直される |
uasort() | 値をユーザー定義の比較関数でソート | キーが保持される点が異なる |
uksort() | キーをユーザー定義の比較関数でソート | ソート対象が「値」ではなく「キー」である点が異なる |
sort() | 値の標準的な比較でソート | 独自の比較ロジックを指定できない点が異なる |
array_multisort() | 複数の配列やキーを組み合わせてソート | より複雑な多次元ソートに対応するが、記述がやや煩雑になる |
よくある落とし穴(注意点)
- キーが保持されないことに気づかない 連想配列のキー(IDや名前など)を保持したまま値をソートしたい場合に誤って
usort()を使ってしまうと、キーが失われてバグの原因になります。キーを保持したい場合はuasort()を使いましょう。 - 比較関数は必ず整数を返す必要がある
true/falseのような真偽値を返すと、PHPのバージョンによっては警告が出たり、意図しないソート結果になったりします。<=>(宇宙船演算子)を使い、確実に整数を返すようにしましょう。 - 降順ソートのために
array_reverse()を使うのは非効率 降順にしたい場合は、比較関数内で$aと$bの順序を入れ替える($b <=> $a)のがシンプルかつ効率的です(例6を参照)。 - 元の配列は直接変更される(参照渡し)
usort()は戻り値として新しい配列を返すのではなく、渡した配列自体を直接書き換えます。元の配列も保持しておきたい場合は、事前にコピーを作成しておく必要があります。 - PHP 8.0未満では安定ソートが保証されない
uasort()やuksort()と同様、PHP 8.0以降で安定ソート(比較関数が0を返す要素同士は元の順序を維持する)が保証されるようになりました。それ以前のバージョンでは注意が必要です。
まとめ
| 観点 | まとめ |
|---|---|
| 何をする関数か | ユーザー定義の比較関数を使って配列の値をソートする |
| 主な用途 | オブジェクトの配列や多次元配列を、複数条件を組み合わせて並び替える |
| キーの扱い | 保持されない(0からの連番に振り直される)— uasort()との最大の違い |
| 安定性 | PHP 8.0以降は安定ソートが保証される |
| 注意点 | キーが失われること、比較関数の戻り値のルール、元の配列が直接変更されること |
usort() は、複雑な独自ロジックで配列を並び替えたいときに最もよく使われる基本のソート関数です。キーを保持する必要があるかどうかを最初に見極め、必要であれば uasort()、不要であれば usort() という形で使い分けることで、意図した通りのデータ構造を維持できます。
