目次
はじめに
今回は、usort()
・uksort()
の基本的な使い方のまとめ、サンプルコード3つを作ってみましたので、ご紹介します。
配列の並べ替えは、WordPress開発、PHPでコードを書いている時などによく発生する作業です。
数列、あいうえお順、アルファベット順などはPHPの標準メソッドで簡単に並べ替えることができます。
しかし、例えば、
- 都道府県順で、特定の県のみ先頭に並び替え
- 都道府県順で、九州エリアの県のみ順序を維持して先頭に並び替え
- 配列の全ての値を任意の順番に並べ替え
のように、通常のPHPの標準メソッドでは並び替えられないようなケースに遭遇した時、ユーザー定義で並べ替えることができる、usort()
・uksort()
は便利なので、ご紹介したいと思います。
前提
まず、下記のような前提に当てはまった場合、usort()
・uksort()
は使う必要はありません。
- 数列、あいうえお順、アルファベット順の並び替えをする場合
→sort()
やcollator_asort()
で並び替えることができます。 - SQLから抽出する時点で並び替えできる場合
→SQLで並び替えることができます。
ので、単純に1,2,3,,,,のような連番のソートや、アルファベット・あいうえお順のソートであれば、上記の方法を取ることが良いです。
配列をソートしたい時は、PHP標準のメソッドで対応できないか等を調べた上で、usort()
・uksort()
を使用すると良いです。
検証環境
検証は、PHP Version 7.3.10で行いました。
usort()
・uksort()
とは配列を並び替えるメソッド
usort()
・uksort()
の定義はPHPのドキュメントサイトに掲載されていますので、下記に引用します。
usort —ユーザー定義の比較関数を使用して、配列を値でソートする https://www.php.net/manual/ja/function.usort.php
uksort — ユーザー定義の比較関数を用いて、キーで配列をソートする https://www.php.net/manual/ja/function.uksort.php
上記の定義にある通り、簡単に言うと、
- 配列の値でソートするのが
usort()
- 配列のキーでソートするのが
uksort()
となります。
そして両方とも「ユーザー定義の比較関数」を使用してソートしていきます。
この、「ユーザー定義の比較関数」とは、usort()
・uksort()
を通して、ユーザー(コード・プログラムを書いている本人)が比較関数(任意・独自の関数・メソッド)を作成・使用することで、ソート(自由な並び順に設定)できるという意味になります。
どのように比較関数を作成するのかというと、コールバックメソッドという技術を使用します。
それでは、基本的な使い方を見ていきます。
※コールバックメソッドがわからない、、、という方はこちらの記事を参照してください。
基本的な使用方法
下記に基本的な使用方法のサンプルコードを用意しました。
公式ページで示されているサンプルコードを自分なりにわかりやすく改変し、コメントアウトを追加しました。
サンプルコードをコピペすると、ソート前、ソート後の実行結果を得ることができます。
それでは見ていきましょう。
usort()
の基本的な使用方法
まず、sort_asce_by_number()
というメソッドを比較関数として作成します。
第1引数$arg1
が第2引数$arg2
より大きい時にTRUE
を返すと、順番は昇順(第1引数$arg1
の配列上の配置は、第2引数$arg2
と入れ替わる)になります。
その後、$numbers
に数値の配列を代入し、まずは、代入した順番通りの配列の値かをprint_r()
で確認できるようにします。
次は、usort()
を使用し作成したsort_asce_by_number()
をコールバック関数として呼び出し、$numbers
の配列のソートを実行するようにし、print_r()
でソートが実行されているかブラウザで実行結果を確認しましょう。
/*Logic
------------------------------------------------------*/
/*
* 比較関数(コールバック関数):数値を比較し昇順にする
* @param int $arg1 比較する対象の値1
* @param int $arg2 比較対象対象の値2
* @return 第1引数が大きい時にTRUEを返す
*/
function sort_asce_by_number(int $arg1, int $arg2): int
{
//昇順:第1引数が大きい時に1を返す
return $arg1 > $arg2;
}
/*View
------------------------------------------------------*/
// $numbers に任意の数値を配列として代入
$numbers = [3, 2, 5, 4, 1];
echo "<p>まず代入した順番通りの値か確認<p>";
echo '<pre>';
print_r($numbers);
echo '</pre>';
// usort()でソート実行
usort($numbers, 'sort_asce_by_number');
echo "<p>配列の値が数値の昇順に順番が並べ替えられる。<p>";
echo '<pre>';
print_r($numbers);
echo '</pre>';
/*=>output
まず代入した順番通りの値か確認
Array
(
[0] => 3
[1] => 2
[2] => 5
[3] => 4
[4] => 1
)
配列の値が数値の昇順に順番が並べ替えられる。
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
)
*/
※サンプルなのでこのような単純なソートにしていますが、本来sort()を使えばOKです。
uksort()
の基本的な使用方法
次に配列のキーで値を並べ替えるuksort()
の基本的な使用方法を見ていきましょう。
まず、上記のusort()
で並べ替えをしたように、sort_asce_by_number()
というメソッドを比較関数として作成します。比較関数の内容は、usort()
サンプルコードにあるsort_asce_by_number()
と同じなので、それを使いまわします。
その後、$fruits
に数値をキーに、果物名の文字列を値にして、連想配列を代入します。
そして、代入した順番通りの連想配列の値かをprint_r()
で確認できるようにします。
次は、uksort()
を使用し作成したsort_asce_by_number()
をコールバック関数として呼び出し、$fruits
の配列のソートを実行するようにし、print_r()
でソートが実行されているかブラウザで実行結果を確認しましょう。
/*Logic
------------------------------------------------------*/
# ・・・省略(usort()サンプルコードにあるsort_asce_by_number()を使用します)
/*View
------------------------------------------------------*/
// $fruits に任意の数値キーに、値に果物の名前を文字列で連想配列として代入
$fruits = [
3 => 'banana',
2 => 'grape',
5 => 'apple',
4 => 'peach',
1 => 'orange',
];
echo "<p>まず代入した順番通りの値か確認<p>";
echo "<pre>";
print_r($fruits);
echo "</pre>";
// uksort()でソート実行
uksort($fruits, 'sort_asce_by_number');
echo "
<p>キーの数値の昇順に順番が並べ替えられる。<br>
果物名の値も同様い並べ替えられる。<p>
";
echo "<pre>";
print_r($fruits);
echo "</pre>";
/*=>output
まず代入した順番通りの値か確認
Array
(
[3] => banana
[2] => grape
[5] => apple
[4] => peach
[1] => orange
)
キーの数値の昇順に順番が並べ替えられる。
果物名の値も同様い並べ替えられる。
Array
(
[1] => orange
[2] => grape
[3] => banana
[4] => peach
[5] => apple
)
*/
※サンプルなのでこのような単純なソートにしていますが、本来ksort()を使えばOKです。
どのように順番を決定しているのか
上記の基本的な使用方法のサンプルコードを見ると、コールバック関数である比較関数がどのように順番を決定しているかという疑問が出てくるかと思います。
なので、どのように順番を決定しているのか検証してみました。
検証方法
検証方法は、sort_asce_by_number()に第1引数と第2引数に数値がどのように代入されているか確認するために、両方の引数をecho
で出力させます。
比較するために、2パターンの検証を行います。
/*Logic
------------------------------------------------------*/
function sort_asce_by_number(int $arg1, int $arg2): bool
{
echo "<pre>【第1引数】{$arg1} 、【第2引数】{$arg2}</pre>";# +追加
return $arg1 > $arg2;
}
/*View
------------------------------------------------------*/
# ・・・省略
検証結果
1回目の配列は$numbers = [3, 1, 4, 2];
の配列で行いました。
# ・・・省略
/*View
------------------------------------------------------*/
//配列例1
$numbers = [3, 1, 4, 2];
// usort()でソート実行
usort($numbers, 'sort_asce_by_number');
/*=>output
【第1引数】3、 【第2引数】1
【第1引数】4、 【第2引数】1
【第1引数】3、 【第2引数】4
【第1引数】4、 【第2引数】2
【第1引数】3、 【第2引数】2
【第1引数】1、 【第2引数】2
*/
表にしてみると、下記のように比較されています。
(※組み合わせの重複を取り除くため、使用しないセルは黒塗りしています。)
このように、配列に設置されている数値同士を比較させて、順番を決めているようです。
次は、$numbers = [2, 1, 3, 4];
の順番で検証を行います。
# ・・・省略
/*View
------------------------------------------------------*/
//配列例2
$numbers = [2, 1, 3, 4];
// usort()でソート実行
usort($numbers, 'sort_asce_by_number');
/*=>output
【第1引数】2、 【第2引数】1
【第1引数】3、 【第2引数】1
【第1引数】2、 【第2引数】3
【第1引数】3、 【第2引数】4
*/
出力結果を見ると、4回しか比較されていませんでした。
表にすると、下記のようになります。グリーンのセルは比較されていない組み合わせのセルになります。
何故、比較されていない組み合わせがあるのだろうと思ったのですが、よく見てみると、4-1
、4-2
の1
と2
は、3回目の比較で3
より小さく、4回目で3
は4
より小さいということがわかっていたからでした。
そのため、わざわざ比較する必要はないため、比較は4回で済んでいます。(数学の問題っぽいですね。。。)
このようにPHP側で必要な値同士で比較がされ、並び順によって比較回数も変わってくることがわかります。
サンプル
それでは作成した3つのサンプルをご紹介していきます。
作成したサンプルは下記です。
- ①都道府県順で、特定の県のみ先頭に並び替え
- ②都道府県順で、九州エリアの県のみ順序を維持して先頭に並び替え
- ③配列の全ての値を任意の順番に並べ替え
①都道府県順で、特定の県のみ先頭に並び替え
47都道府県の配列の中にある「福岡県」を先頭に並び替えるサンプルコードです。配列の中にある1つの値を先頭に並び替えます。
比較関数fukuoka_move_top()
にあるように、第1引数$arg1
が福岡の番号である40の時は1
を返し、第2引数$arg2
が40の時は1
を返すことで、40を先頭に配置することができます。 40以外の値は、第1引数$arg1
が大きい時に1
を返すことで、昇順の並びにしています。
都道府県を選ぶセレクトボックスで福岡県を一番上にしたいケースがあると思いますが、そのような時に便利かもしれません。
/*Logic
------------------------------------------------------*/
/*
* 比較関数(コールバック関数):数値を比較し昇順にする
* @param int $arg1 比較する対象の値1
* @param int $arg2 比較対象対象の値2
* @return 福岡を先頭に、福岡以外の都道府県は数値順になるように値を返す
*/
function fukuoka_move_top(int $arg1, int $arg2): int
{
//第1 or 2引数が福岡の番号の時に、先頭になるように値を返す
if($arg1 == 40) return -1;
if($arg2 == 40) return 1;
//第1 or 2引数が福岡以外の番号は数値順になるように値を返す
return $arg1 > $arg2;
}
/*View
------------------------------------------------------*/
// $prefecture に任意の数値を配列として代入
$prefecture = [
1 => '北海道', //北海道地方
2 => '青森県', 3 => '岩手県', 4 => '宮城県', 5 => '秋田県', 6 => '山形県', 7 => '福島県', //東北地方
8 => '茨城県', 9 => '栃木県', 10 => '群馬県', 11 => '埼玉県', 12 => '千葉県', 13 => '東京都', 14 => '神奈川県', //関東地方
15 => '新潟県', 16 => '富山県', 17 => '石川県', 18 => '福井県', 19 => '山梨県', 20 => '長野県', 21 => '岐阜県', 22 => '静岡県', 23 => '愛知県', //中部地方
24 => '三重県', 25 => '滋賀県', 26 => '京都府', 27 => '大阪府', 28 => '兵庫県', 29 => '奈良県', 30 => '和歌山県', //関西地方
31 => '鳥取県', 32 => '島根県', 33 => '岡山県', 34 => '広島県', 35 => '山口県', //中国地方
36 => '徳島県', 37 => '香川県', 38 => '愛媛県', 39 => '高知県', //四国地方
40 => '福岡県', 41 => '佐賀県', 42 => '長崎県', 43 => '熊本県', 44 => '大分県', 45 => '宮崎県', 46 => '鹿児島県', 47 => '沖縄県' //九州地方・沖縄地方
];
// usort()でソート実行
uksort($prefecture, 'fukuoka_move_top');
echo "<pre>";
print_r($prefecture);
echo "</pre>";
/* => output
Array
(
[40] => 福岡県
[1] => 北海道
[2] => 青森県
[3] => 岩手県
[4] => 宮城県
[5] => 秋田県
[6] => 山形県
[7] => 福島県
[8] => 茨城県
[9] => 栃木県
[10] => 群馬県
[11] => 埼玉県
[12] => 千葉県
[13] => 東京都
[14] => 神奈川県
[15] => 新潟県
[16] => 富山県
[17] => 石川県
[18] => 福井県
[19] => 山梨県
[20] => 長野県
[21] => 岐阜県
[22] => 静岡県
[23] => 愛知県
[24] => 三重県
[25] => 滋賀県
[26] => 京都府
[27] => 大阪府
[28] => 兵庫県
[29] => 奈良県
[30] => 和歌山県
[31] => 鳥取県
[32] => 島根県
[33] => 岡山県
[34] => 広島県
[35] => 山口県
[36] => 徳島県
[37] => 香川県
[38] => 愛媛県
[39] => 高知県
[41] => 佐賀県
[42] => 長崎県
[43] => 熊本県
[44] => 大分県
[45] => 宮崎県
[46] => 鹿児島県
[47] => 沖縄県
)
*/
②都道府県順で、九州エリアの県のみ順序を維持して先頭に並び替え
47都道府県の配列の中にある「九州エリアの県」を順番そのままに先頭に並び替えるサンプルコードです。配列の中にある複数の値を順番そのままに先頭に並び替えます。
比較関数kyushu_move_top()
にあるように、 第1引数$arg1
が九州エリアの番号である40~47
の時は1
を返し、第2引数$arg2
が40~47
の時は1
を返すことで、40~47
の複数の値を先頭に配置することができます。さらに、for文で40~47
の順で値を返す処理を繰り返すことで、40~47
を数値順で先頭に配置することができます。 40~47
の九州エリア以外の番号は、上記のサンプルコードと同様、第1引数$arg1
が大きい時に1を返すことで、昇順の並びにしています。
都道府県を選ぶセレクトボックスで九州エリアを一番上にしたいケースがあると思いますが、そのような時に便利かもしれません。
/*Logic
------------------------------------------------------*/
/*
* 比較関数(コールバック関数):数値を比較し昇順にする
* @param int $arg1 比較する対象の値1
* @param int $arg2 比較対象対象の値2
* @return 九州エリアを順序を維持して先頭に、九州エリア以外の都道府県は数値順になるように値を返す
*/
function kyushu_move_top(int $arg1, int $arg2)
{
//第1 or 2引数が九州エリアの番号は数値順に先頭に配置されるように値を返す
if(
$arg1 >= 40 && $arg1 <= 47 ||
$arg2 >= 40 && $arg2 <= 47
){
for($i = 40; $i <= 47; $i++){
if(
$arg1 != $i &&
$arg2 != $i
) continue;
if($arg1 == $i) return -1;
if($arg2 == $i) return 1;
}
}
//第1 or 2引数が九州エリア以外の番号は数値順になるように値を返す
return $arg1 > $arg2;
}
/*View
------------------------------------------------------*/
// $prefecture に任意の数値を配列として代入
$prefecture = [
1 => '北海道', //北海道地方
2 => '青森県', 3 => '岩手県', 4 => '宮城県', 5 => '秋田県', 6 => '山形県', 7 => '福島県', //東北地方
8 => '茨城県', 9 => '栃木県', 10 => '群馬県', 11 => '埼玉県', 12 => '千葉県', 13 => '東京都', 14 => '神奈川県', //関東地方
15 => '新潟県', 16 => '富山県', 17 => '石川県', 18 => '福井県', 19 => '山梨県', 20 => '長野県', 21 => '岐阜県', 22 => '静岡県', 23 => '愛知県', //中部地方
24 => '三重県', 25 => '滋賀県', 26 => '京都府', 27 => '大阪府', 28 => '兵庫県', 29 => '奈良県', 30 => '和歌山県', //関西地方
31 => '鳥取県', 32 => '島根県', 33 => '岡山県', 34 => '広島県', 35 => '山口県', //中国地方
36 => '徳島県', 37 => '香川県', 38 => '愛媛県', 39 => '高知県', //四国地方
40 => '福岡県', 41 => '佐賀県', 42 => '長崎県', 43 => '熊本県', 44 => '大分県', 45 => '宮崎県', 46 => '鹿児島県', 47 => '沖縄県' //九州地方・沖縄地方
];
// usort()でソート実行
uksort($prefecture, 'kyushu_move_top');
echo "<pre>";
print_r($prefecture);
echo "</pre>";
/* => output
Array
(
[40] => 福岡県
[41] => 佐賀県
[42] => 長崎県
[43] => 熊本県
[44] => 大分県
[45] => 宮崎県
[46] => 鹿児島県
[47] => 沖縄県
[1] => 北海道
[2] => 青森県
[3] => 岩手県
[4] => 宮城県
[5] => 秋田県
[6] => 山形県
[7] => 福島県
[8] => 茨城県
[9] => 栃木県
[10] => 群馬県
[11] => 埼玉県
[12] => 千葉県
[13] => 東京都
[14] => 神奈川県
[15] => 新潟県
[16] => 富山県
[17] => 石川県
[18] => 福井県
[19] => 山梨県
[20] => 長野県
[21] => 岐阜県
[22] => 静岡県
[23] => 愛知県
[24] => 三重県
[25] => 滋賀県
[26] => 京都府
[27] => 大阪府
[28] => 兵庫県
[29] => 奈良県
[30] => 和歌山県
[31] => 鳥取県
[32] => 島根県
[33] => 岡山県
[34] => 広島県
[35] => 山口県
[36] => 徳島県
[37] => 香川県
[38] => 愛媛県
[39] => 高知県
)
*/
③配列の全ての値を任意の順番に並べ替え
ランダムで並べた都道府県をランキング順に並び替えるサンプルコードです。配列の全ての値を任意の順番に並べ替えます。
(※ランキング順は、「地域ブランド調査:都道府県魅力度ランキング」のTOP10を参考にしました)
比較関数sort_ranking()
にあるように、$sorter
の変数配列に並び替えしたいランキングの順番を記述し、array_flip()
で編集配列のキーと値を入れ替えます。
そして、$sorter[$arg1] > $sorter[$arg2]
のように比較して値を返すことで、$sorter
のランキング順に並べ替えを行っています。
規則性がないような並べ替えをしたいケースにはピッタリな方法ですね。
/*Logic
------------------------------------------------------*/
/*
* 比較関数(コールバック関数):配列の都道府県をランキング順にソート
* @param int $arg1 比較する対象の値1
* @param int $arg2 比較対象対象の値2
* @return $sorterの並び順になるように値を返す
*/
function sort_ranking(int $arg1, int $arg2)
{
$sorter = [1, 26, 47, 13, 14, 27, 29, 20, 40, 17];
$sorter = array_flip($sorter);
return $sorter[$arg1] > $sorter[$arg2];
}
/*View
------------------------------------------------------*/
// $prefecture に任意の数値を配列として代入
$prefecture = [
1 => '北海道',
13 => '東京都',
14 => '神奈川県',
17 => '石川県',
20 => '長野県',
26 => '京都府',
27 => '大阪府',
29 => '奈良県',
40 => '福岡県',
47 => '沖縄県',
];
// usort()でソート実行
uksort($prefecture, 'sort_ranking');
echo "<pre>";
print_r($prefecture);
echo "</pre>";
/* => output
Array
(
[1] => 北海道
[26] => 京都府
[47] => 沖縄県
[13] => 東京都
[14] => 神奈川県
[27] => 大阪府
[29] => 奈良県
[20] => 長野県
[40] => 福岡県
[17] => 石川県
)
*/
さいごに
Googleで検索してもなかなか良いサンプルコードに出会えなかったので、今回こちらの記事を作成しました。
お困りの方は是非参考にしていただければと思います。
参考
当社では協力会社様を募集しております。
ご希望の方は、「応募フォーム」よりお申し込みいただけると幸いです。