[PHP]usortとは?独自ロジックで配列の値をソートする方法を徹底解説

PHP

はじめに

これまでの記事では、キーを保持したまま値をソートする 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()複数の配列やキーを組み合わせてソートより複雑な多次元ソートに対応するが、記述がやや煩雑になる

よくある落とし穴(注意点)

  1. キーが保持されないことに気づかない 連想配列のキー(IDや名前など)を保持したまま値をソートしたい場合に誤って usort() を使ってしまうと、キーが失われてバグの原因になります。キーを保持したい場合は uasort() を使いましょう。
  2. 比較関数は必ず整数を返す必要がある true/false のような真偽値を返すと、PHPのバージョンによっては警告が出たり、意図しないソート結果になったりします。<=>(宇宙船演算子)を使い、確実に整数を返すようにしましょう。
  3. 降順ソートのために array_reverse() を使うのは非効率 降順にしたい場合は、比較関数内で $a$b の順序を入れ替える($b <=> $a)のがシンプルかつ効率的です(例6を参照)。
  4. 元の配列は直接変更される(参照渡し) usort() は戻り値として新しい配列を返すのではなく、渡した配列自体を直接書き換えます。元の配列も保持しておきたい場合は、事前にコピーを作成しておく必要があります。
  5. PHP 8.0未満では安定ソートが保証されない uasort()uksort() と同様、PHP 8.0以降で安定ソート(比較関数が0を返す要素同士は元の順序を維持する)が保証されるようになりました。それ以前のバージョンでは注意が必要です。

まとめ

観点まとめ
何をする関数かユーザー定義の比較関数を使って配列の値をソートする
主な用途オブジェクトの配列や多次元配列を、複数条件を組み合わせて並び替える
キーの扱い保持されない(0からの連番に振り直される)— uasort()との最大の違い
安定性PHP 8.0以降は安定ソートが保証される
注意点キーが失われること、比較関数の戻り値のルール、元の配列が直接変更されること

usort() は、複雑な独自ロジックで配列を並び替えたいときに最もよく使われる基本のソート関数です。キーを保持する必要があるかどうかを最初に見極め、必要であれば uasort()、不要であれば usort() という形で使い分けることで、意図した通りのデータ構造を維持できます。

タイトルとURLをコピーしました