[PHP]uasortとは?キーを保持したまま独自ロジックで連想配列を並び替える方法を徹底解説

PHP

はじめに

PHPで配列をソートする際、sort()asort() といった標準の比較ルールでは対応しきれないケースは少なくありません。たとえば「オブジェクトの特定プロパティで並び替えたい」「複数条件を組み合わせて優先順位を決めたい」といった場合には、独自の比較ロジックを指定できるソート関数が必要になります。

uasort() はまさにそのための関数です。名前の “u” は “user-defined”(ユーザー定義)を意味し、”a” は連想配列(associative array)の “キーを保持する” という特徴を表しています。つまり uasort() は、ユーザーが定義した比較関数を使い、かつ元のキーを保持したまま配列を値でソートする関数です。本記事では、その基本的な使い方から実践的な活用例、そして他のソート関数との違いまで詳しく解説します。


関数概要

項目内容
関数名uasort()
所属拡張コア関数(標準で常に利用可能)
シグネチャuasort(array &$array, callable $callback): bool
引数1$array — ソート対象の配列(参照渡し)
引数2$callback — 2つの値を比較するコールバック関数
戻り値成功時に true
対応バージョンPHP 4以降
キーの扱い保持される(インデックスは振り直されない)
類似関数usort()(キーを振り直す)、uksort()(キーでソートする)

ソートの流れ(イメージ図)

  ソート前の配列(キー付き)
  ['banana' => 3, 'apple' => 1, 'cherry' => 2]
                  │
                  ▼
     uasort($array, fn($a, $b) => $a <=> $b)
                  │
   ┌──────────────┴──────────────┐
   │ 値を比較関数で比較し、       │
   │ 昇順に並び替える              │
   │ ※ キーはそれぞれの値に      │
   │    紐付いたまま保持される     │
   └──────────────┬──────────────┘
                  ▼
  ソート後の配列(キーは保持、順序のみ変わる)
  ['apple' => 1, 'cherry' => 2, 'banana' => 3]

ポイントは、元のキー('banana', 'apple', 'cherry')がソート後もそのまま維持されるという点です。もし数値インデックスの配列に対して usort() を使うと、ソート後にインデックスが 0, 1, 2... と振り直されてしまいますが、uasort() では連想配列のキーの意味を保ったまま並び替えることができます。


実践サンプル7選

例1:基本的な使い方

<?php

class BasicUasortDemo
{
    public function sortByValue(array $data): array
    {
        // 値を昇順で比較する単純な比較関数を指定
        uasort($data, fn ($a, $b) => $a <=> $b);
        return $data;
    }
}

$demo = new BasicUasortDemo();
$fruits = ['banana' => 3, 'apple' => 1, 'cherry' => 2];
print_r($demo->sortByValue($fruits));
// ['apple' => 1, 'cherry' => 2, 'banana' => 3]  ← キーが保持されている

例2:商品の在庫データを価格順に並び替えるクラス

<?php

class ProductInventorySorter
{
    /**
     * 商品ID => 価格 の連想配列を、商品IDを保持したまま
     * 価格の安い順に並び替える
     */
    public function sortByPrice(array $products, bool $descending = false): array
    {
        uasort($products, function ($priceA, $priceB) use ($descending) {
            return $descending ? $priceB <=> $priceA : $priceA <=> $priceB;
        });

        return $products;
    }
}

$sorter = new ProductInventorySorter();
$products = ['SKU-001' => 1500, 'SKU-002' => 800, 'SKU-003' => 2400];
print_r($sorter->sortByPrice($products));
// ['SKU-002' => 800, 'SKU-001' => 1500, 'SKU-003' => 2400]

例3:オブジェクトの配列を特定プロパティでソートする

<?php

class Employee
{
    public function __construct(
        public string $name,
        public int $age,
        public float $salary
    ) {
    }
}

class EmployeeSorter
{
    /**
     * 従業員ID => Employeeオブジェクト の連想配列を
     * 年齢でソートする(IDとの対応は保持したまま)
     */
    public function sortByAge(array $employees): array
    {
        uasort($employees, fn (Employee $a, Employee $b) => $a->age <=> $b->age);
        return $employees;
    }
}

$sorter = new EmployeeSorter();
$employees = [
    'E001' => new Employee('田中', 35, 450000),
    'E002' => new Employee('鈴木', 28, 380000),
    'E003' => new Employee('佐藤', 42, 520000),
];

foreach ($sorter->sortByAge($employees) as $id => $employee) {
    echo "{$id}: {$employee->name} ({$employee->age}歳)" . PHP_EOL;
}
// E002: 鈴木 (28歳) → E001: 田中 (35歳) → E003: 佐藤 (42歳)

例4:複数条件で優先順位を決めるソート

<?php

class TaskPrioritySorter
{
    /**
     * まず優先度(数値が小さいほど優先)、
     * 優先度が同じ場合は締切日で比較する
     */
    public function sort(array $tasks): array
    {
        uasort($tasks, function (array $a, array $b) {
            $priorityDiff = $a['priority'] <=> $b['priority'];
            if ($priorityDiff !== 0) {
                return $priorityDiff;
            }
            return strcmp($a['due'], $b['due']);
        });

        return $tasks;
    }
}

$sorter = new TaskPrioritySorter();
$tasks = [
    'task-1' => ['priority' => 2, 'due' => '2026-08-10'],
    'task-2' => ['priority' => 1, 'due' => '2026-08-15'],
    'task-3' => ['priority' => 1, 'due' => '2026-08-05'],
];
print_r($sorter->sort($tasks));
// 'task-3'(優先度1・早い締切) → 'task-2'(優先度1) → 'task-1'(優先度2)

例5:日本語の文字列を文字数順にソートするクラス

<?php

class StringLengthSorter
{
    /**
     * マルチバイト文字列の連想配列を
     * 文字数(mb_strlen)の短い順に並び替える
     */
    public function sortByLength(array $strings): array
    {
        uasort($strings, fn (string $a, string $b) => mb_strlen($a) <=> mb_strlen($b));
        return $strings;
    }
}

$sorter = new StringLengthSorter();
$titles = [
    'id-1' => 'こんにちは世界',
    'id-2' => '猫',
    'id-3' => 'プログラミング入門講座',
];
print_r($sorter->sortByLength($titles));
// 'id-2'(1文字) → 'id-1'(7文字) → 'id-3'(11文字)

例6:安定ソートを保証するための工夫

<?php

class StableUasort
{
    /**
     * PHP 8.0以降のuasort()は安定ソートが保証されているが、
     * 明示的に元の順序を保持したい場合の実装例
     */
    public function stableSortByValue(array $data): array
    {
        // 元のキーとインデックスをペアにして保持しておく
        $indexed = [];
        $i = 0;
        foreach ($data as $key => $value) {
            $indexed[$key] = [$value, $i++];
        }

        uasort($indexed, function ($a, $b) {
            $cmp = $a[0] <=> $b[0];
            // 値が同じ場合は元のインデックス順を維持する
            return $cmp !== 0 ? $cmp : $a[1] <=> $b[1];
        });

        $result = [];
        foreach ($indexed as $key => $pair) {
            $result[$key] = $pair[0];
        }

        return $result;
    }
}

$stableSorter = new StableUasort();
print_r($stableSorter->stableSortByValue(['a' => 2, 'b' => 1, 'c' => 2, 'd' => 1]));

例7:ランキングデータをスコアの高い順に並び替えて上位を抽出する

<?php

class LeaderboardBuilder
{
    /**
     * プレイヤーID => スコア の連想配列を降順にソートし、
     * 上位N件だけを抽出したランキングを作成する
     */
    public function buildTopRanking(array $scores, int $limit = 3): array
    {
        // 降順にするため比較関数の引数の順序を入れ替える
        uasort($scores, fn ($a, $b) => $b <=> $a);

        // キーを保持したままarray_sliceする場合はpreserve_keysをtrueにする
        return array_slice($scores, 0, $limit, true);
    }
}

$builder = new LeaderboardBuilder();
$scores = [
    'player_A' => 8200,
    'player_B' => 9500,
    'player_C' => 7600,
    'player_D' => 9800,
];
print_r($builder->buildTopRanking($scores, 2));
// ['player_D' => 9800, 'player_B' => 9500]

関連関数との比較

関数役割uasortとの違い
uasort()値をユーザー定義の比較関数でソート本記事の対象。キーは保持される
usort()値をユーザー定義の比較関数でソートキーが保持されず、0からの連番に振り直される
uksort()キーをユーザー定義の比較関数でソートソート対象が「値」ではなく「キー」である点が異なる
asort()値の標準的な比較でソート(キー保持)独自の比較ロジックを指定できない点が異なる
arsort()値の標準的な比較で降順ソート(キー保持)uasort()で降順にしたい場合は比較関数の引数順を入れ替える必要がある

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

  1. 比較関数は必ず整数を返す必要がある コールバックは $a$b より小さい場合は負の数、等しい場合は0、大きい場合は正の数を返す必要があります。<=>(宇宙船演算子)を使うとシンプルに実装できます。
  2. usort() と混同してキーが消えることを心配する/逆に期待する キーを保持したいのに usort() を使ってしまうと、連想配列のキー(IDや商品コードなど)が失われて単純な連番になってしまいます。用途に応じて正しい関数を選びましょう。
  3. PHP 8.0未満では安定ソートが保証されない PHP 8.0以降、比較関数が同じ値に対して0を返した場合、元の順序が維持される「安定ソート」が保証されるようになりました。それ以前のバージョンでは同値要素の順序が保証されないため、必要であれば例6のように明示的に安定ソートを実装する工夫が必要です。
  4. 降順ソートのために配列を反転させる必要はない 降順にしたい場合、array_reverse() を後から使うのではなく、比較関数内で $a$b の順序を入れ替える($b <=> $a)だけで実現できます。
  5. 大量データに対するパフォーマンス コールバック関数内で重い処理(データベースアクセスなど)を行うと、要素数に応じて呼び出し回数が増えるため、パフォーマンスに大きく影響します。比較に必要な値は事前に計算・キャッシュしておくことが望ましいです。

まとめ

観点まとめ
何をする関数かユーザー定義の比較関数を使い、キーを保持したまま値でソートする
主な用途連想配列やオブジェクトの配列を、独自条件(価格・年齢・優先度など)で並び替える
キーの扱い保持される(usort()との最大の違い)
安定性PHP 8.0以降は安定ソートが保証される
注意点比較関数の戻り値のルール、usort()との使い分け、降順にする際の実装方法

uasort() は、連想配列のキーという「意味のある情報」を保持しながら、柔軟な独自ロジックでソートできる非常に実用的な関数です。ランキング表示や優先順位付けなど、キーと値の対応関係を崩したくない場面では積極的に活用していきましょう。

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