usortと比較関数の書き方、宇宙船演算子で多段ソート
usortは、自前の比較関数で配列を並べ替える関数です。比較関数は負・ゼロ・正を返すだけで、宇宙船演算子(<=>)を使うと多段ソートも素直に書けますね。
比較関数は負・ゼロ・正を返すだけ
usort は自前の比較関数で並び順を決められる関数ですが、比較関数が何を返せばいいのか最初は迷いますね。ルールは単純で、第一引数を前に置くなら負、後ろなら正、同じなら 0 を返すだけです。昇順なら $a – $b の向きになります。
$nums = [3, 1, 4, 1, 5];
usort($nums, function ($a, $b) {
return $a - $b; // 昇順
});
// [1, 1, 3, 4, 5]
ただし $a – $b の引き算は、値が大きいと桁あふれの懸念がありますし、浮動小数だと差が 0.5 みたいな非整数になって直感的でないこともあります。
宇宙船演算子で安全に書く
PHP7 以降なら宇宙船演算子 <=> を使うのがおすすめです。左右を比較して負・ゼロ・正をよしなに返してくれるので、比較関数がそのまま一行で書けますね。文字列でも数値でも同じように扱えます。
usort($users, fn($a, $b) => $a['age'] <=> $b['age']); // 年齢の昇順
// 降順にしたいなら左右を入れ替えるだけ
usort($users, fn($a, $b) => $b['age'] <=> $a['age']);
多段ソートは演算子をつなぐ
「年齢の昇順、同じ年齢なら名前順」のような複数キーのソートも、宇宙船演算子を並べれば素直に書けます。前のキーが 0(同値)のときだけ次のキーが効く、という短絡評価を ?: でつなぐ形ですね。
usort($users, function ($a, $b) {
return $a['age'] <=> $b['age']
?: $a['name'] <=> $b['name'];
});
キーが増えても同じ調子で ?: をつなげばよいので、拡張しやすい書き方だと思います。ちなみにキーを保持したまま並べたいときは uasort、キーで並べたいときは uksort を使い分けます。
まとめ
usort の比較関数は「負・ゼロ・正を返すだけ」と割り切れば怖くありません。引き算より宇宙船演算子を使うほうが安全で読みやすく、多段ソートも ?: でつなぐだけで済みます。キーを維持したいかどうかで u/ua/uk の三種を選ぶ、という対応も頭の隅に置いておくと便利ですね。
よくある質問
Q. 比較関数は何を返せばいいのですか。
A. 第一引数を前に置くなら負、後ろなら正、同じなら0を返すだけです。宇宙船演算子(<=>)を使うとこれを一行で書けます。
Q. キーを保持したまま並べたいときは。
A. usortはキーを振り直します。キーを維持するならuasort、キーで並べるならuksortを使います。