RitoLabo

PHPでの配列ソート(並び替え)関数まとめ

  • 公開:
  • 更新:
  • カテゴリ: PHP Basics
  • タグ: PHP,sort,Basics,Array

PHPには、配列をソートする(並び替える)為の関数がたくさん存在しています。今回はそれらの微妙な違いを確認しながら、配列を並び替える関数を一通り見ていきます。

アジェンダ
  1. sort/rsort/usort
    1. sort
    2. rsort
    3. usort
  2. asort/arsort/uasort
    1. asort
    2. arsort
    3. uasort
  3. ksort/krsort/uksort
    1. ksort
    2. krsort
    3. uksort
  4. ユーザー定義関数について
  5. natsort/natcasesort
    1. natsort
    2. natcasesort
  6. array_multisort
  7. array_reverse
  8. array_flip
  9. shuffle

sort/rsort/usort

sort系関数であるsort() rsort() usort()は、それぞれを値を基準として並び替えを行います。

「値」というのは、配列の形であるkey => valueの、valueの事です。

sort

sort関数は、対象配列を昇順にソートします。

bool sort ( array &$array [, int $sort_flags = SORT_REGULAR ] )

第一引数には対象配列を渡します。オプションで第二引数にソート処理方式を指定する為の定数を渡せます。

$array = [4, 3, 6, 2, 4, 1, 5];
sort($array);

print_r($array);
// => Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 4 [5] => 5 [6] => 6 )

昇順に並び替えられたのと一緒に、キー(添え字)が振り直されます。ソート系関数は、インデックスが振り直されるかどうかにも注目すると、それらの違いを理解しやすいです。

ちなみにオプションの第二引数には、以下の定数を渡せます。

SORT_REGULAR
通常の比較(型比較無し)でソート
SORT_NUMERIC
数値としての比較でソート
SORT_STRING
文字列として比較でソート
SORT_LOCALE_STRING
設定済みロケールに基づく比較でソート
SORT_NATURAL
文字列+自然順アルゴリズムで比較でソート
SORT_FLAG_CASE
大文字小文字区別無しの比較でソート

SORT_FLAG_CASE は、 SORT_STRING や SORT_NATURAL との組み合わせ(ビットOR演算子でつなぐ)で用いられます

以下ではオプションに定数を渡し、文字列としての比較でソートを行っています。

$array = ['a', 'b', 'c', 'A', 'B', 'C'];

sort($array, SORT_STRING); // 単純な文字列順でソート

print_r($array);
// => Array ( [0] => A [1] => B [2] => C [3] => a [4] => b [5] => c )

以下は大文字小文字の区別無しとして文字列としての比較でソートを行っています。

$array = ['a', 'b', 'c', 'A', 'B', 'C'];

sort($array, SORT_STRING | SORT_FLAG_CASE); // 文字列の大文字小文字を区別しない

print_r($array);
// => Array ( [0] => a [1] => A [2] => b [3] => B [4] => c [5] => C )

両者でソート結果が違う事が確認できます。

rsort

rsort関数は、sort関数の逆で対象配列を降順にソートします。

bool rsort ( array &$array [, int $sort_flags = SORT_REGULAR ] )

引数はsort関数と同じです。

$array = [4, 3, 6, 2, 4, 1, 5];
rsort($array);

print_r($array);
// => Array ( [0] => 6 [1] => 5 [2] => 4 [3] => 4 [4] => 3 [5] => 2 [6] => 1 )

rsort()も、キーは振り直されます。

usort

usort関数は、ユーザ定義関数を用いて対象配列を値でソートします。

/* (PHP 4, PHP 5, PHP 7) */
bool usort ( array &$array , callable $value_compare_func )

第一引数に対象配列を、第二引数にコールバック関数名を渡します。

function ascendingOrder($a, $b)
{
if ($a===$b) {
return 0;
} else if ($a > $b) {
return 1;
} else if ($a < $b) {
return -1;
}
}

$array = [4, 3, 0, 6, 2, 4, 1, 5];

usort($array, "ascendingOrder");

var_dump($array);
// 0 => int 0
// 1 => int 1
// 2 => int 2
// 3 => int 3
// 4 => int 4
// 5 => int 4
// 6 => int 5
// 7 => int 6

コールバック関数は引数を最大2つまで取り、それぞれ、現在の値と、その次の値が渡ります。

例として実装したascendingOrder関数は、昇順ソートの為の基本的な比較アルゴリズムです。

usort()の場合も、キーは振り直される事が確認できます。

asort/arsort/uasort

asort系関数も、sort系関数と同じく値を軸として並び替えを行いますが、sort系関数との違いは、「キーを維持する」点です。

asort

asort関数は、キーと値を維持しつつ、配列を値を軸に昇順でソートします。

/* PHP 4, PHP 5, PHP 7 */
bool asort ( array &$array [, int $sort_flags = SORT_REGULAR ] );

第二引数にはsort関数と同じ定数をオプションとして指定できます。また、sort()と同じく第一引数は参照で渡るので、戻り値を改めて受け取ったりする必要はありません。

$array = [4, 3, 0, 6, 2, 4, 1, 5];
asort($array);
var_dump($array);
// 2 => int 0
// 6 => int 1
// 4 => int 2
// 1 => int 3
// 0 => int 4
// 5 => int 4
// 7 => int 5
// 3 => int 6

上記の通り、キー(添え字)を保ったままソートされている事が確認できます。これは、キーが文字列、連想配列の場合に力を発揮します。

$array = [
'name' => 'ddd',
'age' => 'aaa',
'sex' => 'eee',
'tel' => 'cccc',
'address' => 'bbb'
];

// sort関数の場合
sort($array);
var_dump($array);
// 0 => string 'aaa'
// 1 => string 'bbb'
// 2 => string 'cccc'
// 3 => string 'ddd'
// 4 => string 'eee'

// => キーが振り直されてしまう

// asort関数の場合
asort($array);
var_dump($array);
// 'age' => string 'aaa'
// 'address' => string 'bbb'
// 'tel' => string 'cccc'
// 'name' => string 'ddd'
// 'sex' => string 'eee'

// => キーは維持される

arsort

arsort関数は、asort()の逆で、値を降順でソートします。こちらもキーは維持されます。引数はasort()と同じです。

$array = [
'name' => 'ddd',
'age' => 'aaa',
'sex' => 'eee',
'tel' => 'cccc',
'address' => 'bbb'
];
arsort($array);
var_dump($array);
// 'sex' => string 'eee'
// 'name' => string 'ddd'
// 'tel' => string 'cccc'
// 'address' => string 'bbb'
// 'age' => string 'aaa'

uasort

uasort関数は、キーを維持した状態で値をユーザー定義関数で並び替えます。

bool uasort ( array &$array , callable $value_compare_func );

第一引数に配列(こちらも参照で渡されるため、戻り値を受け取る必要はありません)を、第二引数に関数名を渡します。

以下は、基本的な降順アルゴリズムを定義したユーザ定義関数で並び変えています。

function descendingOrder($a, $b)
{
if ($a===$b) {
return 0;
} else if ($a > $b) {
return -1;
} else if ($a < $b) {
return 1;
}
}

$array = [
'name' => 'ddd',
'age' => 'aaa',
'sex' => 'eee',
'tel' => 'ccc',
'address' => 'bbb'
];

uasort($array, 'descendingOrder');
var_dump($array);
// 'sex' => string 'eee'
// 'name' => string 'ddd'
// 'tel' => string 'ccc'
// 'address' => string 'bbb'
// 'age' => string 'aaa'

ksort/krsort/uksort

ksort系関数は、キーを軸として並び替えを行います。こちらも、キーは維持されます。

ksort

ksort関数は、配列のキーを軸に昇順で並び替えます。

bool ksort ( array &$array [, int $sort_flags = SORT_REGULAR ] );

こちらも同じく、第一引数に配列を渡し、第二引数にはオプションでソート挙動を変更する定数を渡せます。

$array = [
'ddd' => 1,
'aaa' => 2,
'eee' => 3,
'ccc' => 4,
'bbb' => 5
];
ksort($array);
var_dump($array);
// 'aaa' => int 2
// 'bbb' => int 5
// 'ccc' => int 4
// 'ddd' => int 1
// 'eee' => int 3

キーを軸として昇順に並び替えられている事が確認できます。

krsort

krsort関数はksort()の逆です。キーを軸として、降順でソートを行います。

$array = [
'ddd' => 1,
'aaa' => 2,
'eee' => 3,
'ccc' => 4,
'bbb' => 5
];
krsort($array);
var_dump($array);
// 'eee' => int 3
// 'ddd' => int 1
// 'ccc' => int 4
// 'bbb' => int 5
// 'aaa' => int 2

キーを軸に降順でソートされている事が確認できます。

uksort

uksort関数は、ユーザ定義関数を用いて、キーを軸に配列を並び替えます。

bool uksort ( array &$array , callable $key_compare_func );

第一引数に配列を、第二引数に関数名を渡します。

function user_sort( $a, $b )
{
// eee を先頭に
if ($a === 'eee') {
return -1;
} else if ($b === 'eee') {
return -1;
}

if ($a === $b) {
return 0;
}
// それ以外は比較順
return ( $a < $b ) ? -1: 1;
}

$array = [
'ddd' => 1,
'aaa' => 2,
'eee' => 3,
'ccc' => 4,
'bbb' => 5
];
uksort($array,'user_sort');
var_dump($array);
// 'eee' => int 3
// 'aaa' => int 2
// 'bbb' => int 5
// 'ccc' => int 4
// 'ddd' => int 1

ユーザー定義関数について

usort() uasort() uksort() について、ユーザ定義関数を定義する事で任意の並び替えを行う事が出来ると紹介しましたが、定義する関数には一定のルールが存在します。

ユーザ定義関数には配列内の2つの値(uksort()の場合はキー)が渡されてくるので、それらの比較を行い、以下の基準に従って値を返す必要があります。​

  • 第1引数の値 < 第2引数の値:負の数
  • 第1引数の値 = 第2引数の値:0
  • 第1引数の値 > 第2引数の値:正の数

既にコードとして登場していますが、例としては以下のような形になります。

function ascendingOrder($a, $b)
{
if ($a===$b) {
return 0;
} else if ($a > $b) {
return 1;
} else if ($a < $b) {
return -1;
}
}

引数に2つの値が渡ってくると紹介しましたが、具体的に何が渡ってくるのかと言うと、「配列の値」です。これらが順番でぐるぐると渡され(総当たりではありません)互いに比較され、全ての配列の値の順番が確定されます。

なので、ただ数値を比較するだけなら別にユーザ定義関数でソートしなくても良い。という事になり、それよりも一歩踏み込んだ比較でソートを行いたい場合などに用いる事で力を発揮します。

例えば、以下のようなソートを行いたい場合などは、ユーザ定義関数でのソートが力を発揮します。

function i_love_five($a, $b)
{
if ($a===5) {
return 1;
} else if ($b===5) {
return 1;
}

if ($a===$b) {
return 0;
} else {
return($a < $b) ? -1 : 1;
}

}

$array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
usort($array, "i_love_five");
var_dump($array);
// 0 => int 5
// 1 => int 1
// 2 => int 2
// 3 => int 3
// 4 => int 4
// 5 => int 6
// 6 => int 7
// 7 => int 8
// 8 => int 9
// 9 => int 10

usort()を用いて昇順でのソートを行いますが、とにかく5だけは優先して先頭に持っていきたい!という、(ちょっと普通じゃ絶対やらないような例ですが)こういうイレギュラーなソートを行ったりできるのも、ユーザ定義関数を用いてこそ行える事です。

もちろん数値だけではなく、文字列の比較も行えるので、使い方は無限大です。あとは単純な比較だけではなく、引数の文字列から正規表現などで数値部分を抜き出してそれをソートの基準にしたりなど、発想次第でいくらでも定義する事が出来ます。ユーザ定義関数を用いたソートは、こんな風にして使用されます。

natsort/natcasesort

natsort系関数は、自然順アルゴリズムを用いて配列をソートします。

natsort

natsort関数は、自然順アルゴリズムで配列をソートします。

/* PHP 4, PHP 5, PHP 7 */
bool natsort ( array &$array );

自然順アルゴリズムとは、自然数を数的な順序で並び替えを行う比較アルゴリズムです。例を見るとわかりやすいです。

$array = ['a_1.php', 'a_3.php', 'a_5.php', 'a_7.php', 'a_9.php', 'a_10.php', 'a_13.php', 'a_15.php'];

// 通常のソート
sort($array);
var_dump($array);
// => 'a_1.php'
// => 'a_10.php'
// => 'a_13.php'
// => 'a_15.php'
// => 'a_3.php'
// => 'a_5.php'
// => 'a_7.php'
// => 'a_9.php'

// 自然順アルゴリズムを用いたソート
natsort($array);
var_dump($array);
// => 'a_1.php'
// => 'a_3.php'
// => 'a_5.php'
// => 'a_7.php'
// => 'a_9.php'
// => 'a_10.php'
// => 'a_13.php'
// => 'a_15.php'

上記のように、自然順アルゴリズムを用いる事で自然数順で並び替えを行う事が出来ます。

natcasesort

natcasesort関数は、挙動はnatsortと同じですが、こちらは大文字と小文字を区別せずにソートを行います。

$array = ['a_1.php', 'A_3.php', 'a_5.php', 'A_7.php', 'a_9.php', 'A_10.php', 'a_13.php', 'A_15.php'];

// natsort
natsort($array);
var_dump($array);
// => 'A_3.php'
// => 'A_7.php'
// => 'A_10.php'
// => 'A_15.php'
// => 'a_1.php'
// => 'a_5.php'
// => 'a_9.php'
// => 'a_13.php'

// natcasesort
natcasesort($array);
var_dump($array);
// => 'a_1.php'
// => 'A_3.php'
// => 'a_5.php'
// => 'A_7.php'
// => 'a_9.php'
// => 'A_10.php'
// => 'a_13.php'
// => 'A_15.php'

natsort関数の場合はまず「A」と「a」が比較されるため、接尾辞の自然数は順番にはなりませんが、natcasesortは大文字小文字を区別しないので、自然数順に並びます。

array_multisort

array_multisort関数は、複数の配列に対して一度にソートを行います。

bool array_multisort ( array &$array1 [, mixed $array1_sort_order = SORT_ASC [, mixed $array1_sort_flags = SORT_REGULAR [, mixed $... ]]] );

第一引数に主としてソートを行う配列、オプションで第二引数にソートの方向(SORT_ASC=昇順 / SORT_DESC=降順 | 省略可能・デフォルト=SORT_ASC)を、第三引数に挙動を変更する定数(sort関数と同じ)設定(省略可能・デフォルトはSORT_REGULAR)できます。また、第四引数以降には、2つ目以降の配列を任意の数だけセットする事が出来ます。

$a = [‘c’, ‘d’, ‘a’, ‘b’];
$b = [1, 2, 3, 4];
array_multisort($a, $b);
var_dump($a);
// 0 => string '‘a’'
// 1 => string '‘b’'
// 2 => string '‘c’'
// 3 => string '‘d’'
var_dump($b);
// 0 => int 3
// 1 => int 4
// 2 => int 1
// 3 => int 2

ここで一つ注意点があります。変数$bの結果を確認すると、昇順で並んでいない事がわかります。

このarray_multisort関数は、渡した全ての配列をそれぞれ昇順・降順でソートするわけではなく、主たる配列、つまりは最初に渡した配列の並び替え結果に応じて、それと同じ並び替えを2つめ以降の配列に適用するものになります。例えば、以下のようにするとイメージしやすいでしょう。

$a = [‘c’, ‘d’, ‘a’, ‘b’];
$b = [3, 4, 1, 2];
array_multisort($a, $b);
var_dump($a);
// 0 => string '‘a’'
// 1 => string '‘b’'
// 2 => string '‘c’'
// 3 => string '‘d’'
var_dump($b);
/// 0 => int 1
// 1 => int 2
// 2 => int 3
// 3 => int 4

並び替え対象の主たる配列である配列$aにおいて、3番目を先頭に、4番目を2番目に、1番目を3番目に、2番目を4番目へ移動する事で昇順に並べ替えていますが、2つ目以降である配列$bに対しても、同じ移動方式で並べ替えを行っています。それを見越してソート前の配列を意図的に並べ替えておいたので、ソートを適用させた結果、変数$bも綺麗に昇順に並べ替えられています。

では、見方によってはかなり中途半端な挙動をするこの関数は、どのような状況下で使えるのかというと、例えば以下のようなケースで力を発揮します。

$a = ['Japan', 'America', 'England', 'Italy', 'France'];
$b = ['Tokyo', 'Washington, D.C.', 'London', 'Roma', 'Paris'];
array_multisort($a, $b);
var_dump($a);
// 0 => string 'France'
// 1 => string 'America’
// 2 => string 'England’
// 3 => string '‘Italy’'
// 4 => string '‘Japan’'
var_dump($b);
// 0 => string 'Paris'
// 1 => string 'Washington, D.C.'
// 2 => string 'London'
// 3 => string 'Roma'
// 4 => string 'Tokyo'
変数$aには国名が、変数$bには首都名が格納されており、それぞれの配列の位置が対になっています。これは一例ですが、こういった、バラバラになっているが本来であればペアである配列同士をずれる事なく並べ替えを行えるのも、この関数の使えるところでもあります。

array_reverse

array_reverse関数は、配列を逆順に並び替えた配列を返します。

/* PHP 4, PHP 5, PHP 7 */
array array_reverse ( array $array [, bool $preserve_keys = FALSE ] );

第一引数に配列を、第二引数にはオプションキーを保持するかどうかを真偽値で渡します。デフォルトはfalse(キーを保持しない=振り直される)です。

$a = ['Japan', 'America', 'England', 'Italy', 'France'];

// 第二引数を省略するとデフォルトでfalseがセットされ、キーは振り直されます。
$reverse = array_reverse($a);
var_dump($reverse);
// 0 => string 'France'
// 1 => string 'Italy'
// 2 => string 'England'
// 3 => string 'America'
// 4 => string 'Japan'

// trueを渡すとキーは維持されます。
$reverse = array_reverse($a, true);
var_dump($reverse);
// 4 => string 'France'
// 3 => string 'Italy'
// 2 => string 'England'
// 1 => string 'America'
// 0 => string 'Japan'

array_flip

array_flip関数は、配列のキーと値を反転させます。

/* PHP 4, PHP 5, PHP 7 */
array array_flip ( array $array );

キーと値をそっくり入れ替えるのがこの関数の特徴です。

$array = [
'Japan' => 'Tokyo',
'America' => 'Washington, D.C.',
'England' => 'London',
'Italy' => 'Roma',
'France' => 'Paris'
];

$replace = array_flip($array);
print_r($replace);
// => Array
//(
// [Tokyo] => Japan
// [Washington, D.C.] => America
// [London] => England
// [Roma] => Italy
// [Paris] => France
//)

元の配列はキーが国、値が首都でしたが、反対になっている事が確認できます。

shuffle

shuffle関数は、配列をランダムに並び替えます。

/* PHP 4, PHP 5, PHP 7 */
bool shuffle ( array &$array );

引数に渡す配列は参照扱いになるので、戻り値をキャッチしなくても配列内はシャッフルされます。

$array = [4, 3, 0, 6, 2, 4, 1, 5];
shuffle($array);
var_dump($array);
// 0 => int 3
// 1 => int 5
// 2 => int 4
// 3 => int 1
// 4 => int 4
// 5 => int 2
// 6 => int 0
// 7 => int 6

ちなみにこの関数はキーを振り直すので、文字列キーを持つ配列を渡す場合などは注意が必要です。

$array = [
'Japan' => 'Tokyo',
'America' => 'Washington, D.C.',
'England' => 'London',
'Italy' => 'Roma',
'France' => 'Paris'
];
shuffle($array);
var_dump($array);
// 0 => string 'Roma'
// 1 => string 'Tokyo'
// 2 => string 'Paris'
// 3 => string 'London'
// 4 => string 'Washington, D.C.'

まとめ

このように、配列をソートする関数はたくさんありますが、それぞれの特徴を掴み、違いを理解することで、数のわりにケースによって使うべき関数は限定される事がわかると思います。

古くからあるこのレガシーな関数たちは、これからもずっとPHPの機能となり続けるでしょう。是非試してみてください。