Laravel Collection 並び替えやグループ化 sort/groupby/pluckなど
LaravelのCollectionとは
LaravelのCollectionは、PHPの配列をより便利に使うためのラッパークラスです。
前回の記事ではCollectionの基本、toArray、map、each、filter、whereメソッドをご紹介しました。
Collectionのメソッドはまだたくさんありますが、その知識を習得すればするほど、実務において非常に役立つと感じられるでしょう。
この記事では新たに、以下のメソッドを学んでいきましょう。
- sort
- sortBy
- count
- groupBy
- pluck
sort
sort
メソッドは、要素を並べ替えるために使います。
例えば、ばらばらの大きさで並んでいる数値の配列を、昇順(小さい順)に並べ替えるときに役立ちます。
前回の記事でお話ししたように、このメソッドも元のCollectionに変更を加えずに、新しいCollectionを返します。
$numbers = collect([3, 1, 5, 2, 4]);
$asc = $numbers->sort();
/*
$asc->toArray():
[
1 => 1,
3 => 2,
0 => 3,
4 => 4,
2 => 5,
]
*/
降順(大きい順)に並べ替えたいときはsortDesc
メソッドを使います。
$desc = $numbers->sortDesc();
/*
$desc->toArray():
[
2 => 5,
4 => 4,
0 => 3,
3 => 2,
1 => 1,
]
*/
注意していただきたいのは、確かに並べ替えられたものの、キーは元のままである点です。
連番の配列がほしいときはどうすればいいのでしょうか。
キーを連番にリセットするvaluesメソッド
順番は変わったのにキーが元のままということは、Collectionではよくあることです。
こういうときは、キーを連番にリセットするvalues
メソッドの出番です。
sort
に続けて使ってみましょう。
$numbers = collect([3, 1, 5, 2, 4]);
$sorted = $numbers->sort()->values();
/*
$sorted->toArray():
[
0 => 1,
1 => 2,
2 => 3,
3 => 4,
4 => 5,
]
*/
sortにクロージャを渡す
sort
で並べ変えることができるのは数値だけではありません。
sort
は文字列も並べ替えてくれます。
$events = collect([
'11/23 勤労感謝の日',
'5/5 こどもの日',
'1/1 元日',
'11/3 文化の日',
]);
$sorted = $events->sort()->values();
/*
$sorted->toArray():
[
0 => "1/1 元日",
1 => "11/23 勤労感謝の日",
2 => "11/3 文化の日",
3 => "5/5 こどもの日",
]
*/
並べ替えられましたが違和感があります。
機械的な文字の順番としてはこの並びが正しいのですが、文字列の先頭が日付であるという人間の意図が反映されていません。
こういうときはsort
にクロージャを渡すことで、並び替えの条件を具体的に指定することができます。
// 例えば'1/1 元日'を'01/01 元日'に変換する
function padDate($str) {
$exploded = explode(' ', $str); // ['1/1', '元日']
$name = $exploded[1];
$exploded2 = explode('/', $exploded[0]); // ['1', '1']
$mon = Str::padLeft($exploded2[0], 2, '0');
$day = Str::padLeft($exploded2[1], 2, '0');
return "$mon/$day $name";
}
$sorted2 = $events->sort(function($a, $b) {
return strcmp(padDate($a), padDate($b));
})->values();
/*
$sorted2->toArray():
[
0 => "1/1 元日",
1 => "5/5 こどもの日",
2 => "11/3 文化の日",
3 => "11/23 勤労感謝の日",
]
*/
sortBy
key=>value形式のCollectionを昇順に並べ替えたいときは、sortBy
メソッドが便利です。
降順に並べ替えるsortByDesc
メソッドもあります。
$users = collect([
['name' => 'サザエ', 'gender' => 'female', 'age' => 24],
['name' => '波平', 'gender' => 'male', 'age' => 54],
['name' => 'フネ', 'gender' => 'female', 'age' => 50],
['name' => 'マスオ', 'gender' => 'male', 'age' => 28],
]);
$sorted = $users -> sortBy('age');
/*
$sorted->toArray():
[
['name' => 'サザエ', 'gender' => 'female', 'age' => 24],
['name' => 'マスオ', 'gender' => 'male', 'age' => 28],
['name' => 'フネ', 'gender' => 'female', 'age' => 50],
['name' => '波平', 'gender' => 'male', 'age' => 54],
]
*/
count
count
メソッドは、Collection内の要素の数を返します。
$items = collect(['apple', 'banana', 'orange']);
$count = $items->count(); // $countには3が代入される
groupBy
groupBy
メソッドは、key=>value形式のCollectionの要素を指定したキーに基づいてグループ化します。
例えば、ユーザーを男女に分けたいときに便利です。
$users = collect([
['name' => 'サザエ', 'gender' => 'female', 'age' => 24],
['name' => '波平', 'gender' => 'male', 'age' => 54],
['name' => 'フネ', 'gender' => 'female', 'age' => 50],
['name' => 'マスオ', 'gender' => 'male', 'age' => 28],
]);
$grouped = $users->groupBy('gender');
/*
$grouped->toArray():
[
'female'=> [
['name' => 'サザエ', 'gender' => 'female', 'age' => 24],
['name' => 'フネ', 'gender' => 'female', 'age' => 50],
],
'male' => [
['name' => '波平', 'gender' => 'male', 'age' => 54],
['name' => 'マスオ', 'gender' => 'male', 'age' => 28],
]
]
*/
pluck
pluck
メソッドも、key=>value形式のCollectionに対して使うメソッドで、特定のkeyを持つ値だけを抽出してCollectionを作ってくれます。
例えば、ユーザー情報が手元にあるけど、そこからユーザー名だけの配列を作りたいというときに便利です。
$users = collect([
['name' => 'サザエ', 'gender' => 'female', 'age' => 24],
['name' => '波平', 'gender' => 'male', 'age' => 54],
['name' => 'フネ', 'gender' => 'female', 'age' => 50],
['name' => 'マスオ', 'gender' => 'male', 'age' => 28],
]);
$names = $users->pluck('name');
/*
$names->toArray():
['サザエ', '波平', 'フネ', 'マスオ']
*/
組み合わせて使ってみよう
Collectionのメソッドは単発で使うだけでも、生の配列を処理するよりも簡潔に書けますが、組み合わせて使うとき、特にその簡潔さが際立ちます。
例えば、ユーザーの年齢の昇順の配列が欲しいとき、このように書くことができます。
$users
->pluck('age')
->sort()
->values()
->toArray();
/*
[24, 28, 50, 54]
*/
次に、ユーザーを若い順に並び替え、男女別に分けてみましょう。
$names = $users
->sortBy('age')
->groupBy('gender')
->toArray();
/*
[
'female'=> [
['name' => 'サザエ', 'gender' => 'female', 'age' => 24],
['name' => 'フネ', 'gender' => 'female', 'age' => 50],
],
'male' => [
['name' => 'マスオ', 'gender' => 'male', 'age' => 28],
['name' => '波平', 'gender' => 'male', 'age' => 54],
]
]
*/
いずれの場合も、生の配列でやろうと思うと、書くのも読むのも億劫なコードになってしまいます。
Collectionを上手に使うことで、きれいで保守性の高いコードを書くことを心がけましょう。