Laravelのコレクション(その1:概要編)

0
158

Laravelでは様々な便利機能を用意してくれていますが、特にコレクションに関する習熟度は品質・生産性に大きく影響与えると思っています。

コレクションは簡単に言えば配列(連想配列含む)に対して同操作のための便利メソッドを多数提供してくれるラッパーオブジェクトです。
特にDBからの情報取得においてはモデル(Eloquent)を要素とするコレクションになるケースが多いため、コレクションを扱えることはDBを扱うための必須条件とも言えます。

コレクションには100以上のメソッドが用意されています。それ故に普段使っているのは一部の馴染みのある機能だけで、本来はコネクションのメソッドで簡単に実装できる処理を自製したり、漠然と覚えていた機能を使いたくなった段階で改めてネットで調べる必要が生じたりと、生産性・品質においてコネクションの恩恵を十分享受できていないケースも多いのではないでしょうか?(少なくとも私はそうです)。
これでは宝の持ち腐れと言うものです。

と言うことで、一度きっちりとコレクションの各機能に関して確認しておきたいと思います。
ただ、前述のように数が多く一度に整理するのは難しいため、何回かに分けて整理して行きます。
なお、対象となるLaravelのバージョンは現在のLTSである6.xです。

今回は初回と言うことで、まずはコレクションが持つメソッド全体の概要整理を行いたいと思います。
コレクションに関しては、最終的には全メソッドに関して細かな用法含めて記憶し、ネット等に頼らなくても使えるようになることを目指したいと思っていますが、その第一歩としてまずはメソッド名とざっくりとした用途を網羅的に把握しておく必要があると思います。
本記事では上記学習の資料的な位置付けとして、各メソッドを用途によって分類すると共にメソッド名と簡単な機能説明のセットを列記して行きます。前述したように目指すは「記憶」であるため、機能説明はあくまで記憶を呼び起こす程度の簡素なものにします。表現の正確性に関してはかなり怪しいものになりますが、その点はご容赦を。

また、先ほどさらっと「DBを扱う際にコレクションは必須」と言うようなことを書きましたが、モデル(Eloquent)のプロパティやメソッドおよびクエリの実行結果として取得されるコレクションは、実はオリジナルのコレクション(Illuminate\Support\Collection)ではなく、Eloquentコレクション(Illuminate\Database\Eloquent\Collection)と言うもののようです。
Eloquentコレクションも通常のコレクションを継承し機能拡張したものであるため、一般のコレクションとして整理した知識が概ねそのまま適用できます。よって、基本的には両者を区別しませんが、特に注意が必要なケースがあればその都度触れたいと思います。

なお、本記事および関連する記事においてはコレクションの元となった配列を「本体配列」と表記します。

要素の取得

配列でもそうですが、インデックスやキーによって特定の要素を取得する操作は基本中の基本です。
要素の取得に該当するメソッドを以下に列記します。

first最初の要素を取得(コールバックで条件指定も可能)
firstWhere条件に合う最初の要素を取得
getキーもしくはインデックスを指定して要素を取得
last最後の要素を取得(コールバックで条件指定も可能)
pop最後の要素を取得しながらコレクションから削除
pullキーを指定して要素を取得しながらコレクションから削除
randomランダムに要素を取得
search指定した値を持つ要素のインデックスを取得
shift最初の要素を取得しながらコレクションから削除

全データ取得

個別の要素ではなくコレクションが持つ情報全体を取得したいこともあります。
単純に本体配列をそのまま配列の形で取得することもできますが、JSONエンコードされた形や文字列化して取得することも可能です。
そのようなコレクションの情報全体を所定の形式で取得するメソッドを以下に列記します。

all配列形式で取得(2次元以降のデータ型はそのまま)
join指定したセパレータで要素を結合した文字列を取得(implodeより若干複雑な指定が可能)
toArray配列形式で取得(2次元以降も変換)
toJsonJSON形式で取得

サブセット取得

様々な条件に基づいてコレクションが持つ要素の一部だけを取得したいケースも結構あります。
そのような元コレクションのサブセットを取得するようなメソッドを以下に列記します。

diff指定したデータに含まれない要素を取得
diffAssoc指定したデータのキーと値の組み合わせが該当しない要素を取得
diffKeys指定した連想配列のキーと合致しないキーの要素を取得
duplicates重複した要素を取得
duplicatesStrictduplicatesの厳密比較版
except指定したキーと合致しないキーの要素を取得
filter各要素に対してコールバックを呼び出し、戻り値がtrueの要素を取得
forPageページ単位の要素を取得
implode指定したセパレータで要素を結合した文字列を取得する
intersect指定したデータに含まれる要素を取得
intersectByKeys指定した連想配列のキーと合致するキーの要素を取得
nthn番目の要素を取得
only指定したキーの要素を取得
partition指定した条件で要素を2つのコレクションに分ける
reject各要素に対してコールバックを呼び出し、戻り値がfalseの要素を取得(filterの逆)
skip先頭から指定の数だけ要素を除外して取得
slice指定したオフセットから指定した数だけ要素を取得
splice指定したオフセットから指定した数だけ要素を除外して取得(sliceの逆)
take先頭から指定した数の要素を取得
unique重複を除外して要素を取得
uniqueStrictuniqueの厳密比較版
where指定した条件に合致する要素を取得
whereStrictwhereの厳密比較版
whereBetween指定した2つの値の間に含まれる値を持つ要素を取得
whereIn指定した値(複数)に含まれる値を持つ要素を取得
whereInStrictwhereInの厳密比較版
whereInstanceOf指定したクラスの要素を取得
whereNotBetween指定した2つの値の間に含まれない値を持つ要素を取得
whereNotIn指定した値(複数)に含まれない値を持つ要素を取得
whereNotInStrictwhereNotInの厳密比較版
whereNotNull指定のメンバがNULL以外の要素を取得
whereNull指定のメンバがNULLの要素を取得

ソート

複数の要素が並んでいる構造においては、やはり並べ替え(ソート)は考えたくなります。
そのような並べ替えに関するメソッドを以下に列記します。

reverse並び順を反転させる
shuffle並び順をシャッフルする
sort昇順にソートする
sortBy指定のメンバの値で昇順にソートする
sortByDesc指定のメンバの値で降順にソートする
sortKeysキーで昇順にソートする
sortKeysDescキーで降順にソートする
valuesインデックスを振り直す

要素の追加・変更・削除

配列に対して要素を追加したり一部要素の内容を変更、削除すると言うことも頻繁に行われる操作です。
そのような要素の追加・変更・削除メソッドを以下に列記します。

concatコレクションの末尾に複数要素を追加する
forget指定した要素を削除する
pad要素のパディング
prependコレクションの先頭に複数要素を追加する
pushコレクションの末尾に1要素を追加する
putキーと値を追加する

構造変換

既存コレクションをあくまで「元データ」や「材料」として、場合によってはデータを追加しながら、新たな構造を持つコレクションを生成すると言うこともよくあります。
そのような構造変換メソッドを以下に列記します。

chunk指定の数ごとに要素を分割する
collapse多次元配列を一次元配列にする
combine元コレクションの要素をキー、指定の配列の要素を値として連想配列生成
crossJoin元コレクションの要素と指定の配列の要素の全ての組み合わせの配列生成
flatMap全要素に対するコールバック関数による加工(結果は一次元配列)
flatten多次元連想配列を一次元配列にする
flipキーと値を入れ替える
groupBy指定のキーの値でグループ化
keyBy指定のキーの値で配列化(各要素は同じ値を持つ最後の要素)
keysキーのみを要素に持つ連想配列を取得
map全要素に対するコールバック関数による加工
mapInto全要素を特定クラスのインスタンスに変換
mapSpread二次元配列の二次元目の配列の要素を変数とするコールバック関数による加工
mapToGroupsgroupByの機能+コールバックでの各要素に関する任意の加工
mapWithKeys全要素に対するコールバック関数による連想配列への変換
merge指定した配列をコレクションにマージ(キーが同じ場合は上書き)
mergeRecursive指定した配列をコレクションにマージ(キーが同じで値が異なる要素は配列化)
pluck指定したキーの値を配列化
replacemergeの変化形として、キーではなくインデックスで対象要素を指定(対象指定は一次元)
replaceRecursivereplaceの変化形として、多次元で対象指定可能
split指定した数のグループに分割
transform全要素に対するコールバック関数による加工(変更結果を自身に反映)
union指定した配列をコレクションにマージ(キーが同じ場合は元の値を残す)
zipコレクションの各値と指定した配列内の同インデックスの値をセットとする二次元配列化

集計・分析

特に数値データが複数存在する状況においては、合計や平均値などを求めたいケースも多いです。
ここではそのような集計・分析メソッドを以下に列記します。

avg(average)平均値を取得
count要素数を取得
countBy同じ値に対するそれぞれの要素数を取得
max最大値を取得
median中央値を取得
min最小値を取得
mode最頻値を取得
sum合計値を取得

要素の有無

コレクションが特定の条件に該当するかどうかをチェックし、結果を真偽で返す機能も存在します。
そのようなチェックメソッドを以下に列記します。

contains(some)特定の条件を満たす要素が含まれているかどうかをチェック
containsStrictcontainsの厳密比較版
every全ての要素が条件を満たすかをチェック
has特定のキーが含まれるかをチェック
isEmpty中身が空かをチェック
isNotEmpty中身が空でないかをチェック

各要素に対する操作

単純に各要素に関してコールバックを呼び出すだけの機能も存在します。コールバックに関しては特に戻り値を期待するものでもなく単に実行するだけなので、表示を行うか、use文で配列やオブジェクトを参照渡しすることでコールバック内での処理結果をコールバック外に持ち出せるようにして使う必要があるかと思います。
そのような各要素に対してコールバックを呼び出すだけのメソッドを以下に列記します。

each各要素に関するコールバック実行
eachSpread各要素の値を引数とするコールバック実行

条件分岐

条件によってコールバックを実行するかどうかを切り替える処理も存在します。コールバックの引数はいずれも自コレクションのリファレンスであり、必要に応じて自身を加工することを目的とする機能であるように思われます。
そのような条件分岐メソッドを以下に列記します。

unless第一引数が偽の場合のみコールバック実行
whenEmpty(unlessNotEmpty)コレクションが空の場合のみコールバック実行
whenNotEmpty(unlessEmpty)コレクションが空出ない場合のみコールバック実行
when第一引数が真の場合のみコールバック実行

コレクション生成

コレクションを使用する際にコレクションを生成する機能は基本中の基本に思えますが、実は意外に使うケースは少ないです。Laravelが提供する機能の戻り値がコレクションであり、自製処理内では同コレクションに対する操作から行うケースが大半だからです。
と言うことで使う機会は限定的かと思いますが、コレクション生成に関わるメソッドを以下に列記します。

collect同じ要素を持つ新しいコレクション生成
make新規コレクション生成(static)
times特定の要素数の新規コレクション生成(static)
unwrap可能であれば本体配列の取得(static)
wrap可能であればコレクションでラップ(static)

内容表示

ほぼデバッグ用だと思いますが、コレクションの内容を表示する機能もあります。
そのような表示メソッドを以下に列記します。

ddコレクションの中身を表示し、処理を停止
dumpコレクションの中身を表示

その他

今まで紹介したカテゴリに含まれないような特殊なメソッドを以下に列記します。

macro独自のCollectionメソッドを登録
pipeコールバックに自身(コレクション)を渡し、処理結果を返す
reduceコールバックに対して前の要素の処理結果を渡し、同処理結果をさらに次の要素の処理に引き継ぐことで全要素に対して実施した処理結果を返す
tapメソッドチェーンの途中におけるコレクションを取得(同処理内での操作は元のコレクションには影響しない)

総括

コレクションのメソッドに関しては一言では説明し難い物も多く、改めて見返してみても本記事だけ見て各メソッドの機能が想像できる物ではないと言うことがよく分かります。

最初に書いたように、本記事はコレクションの全メソッドが俯瞰的に確認できる程度を目指しており、あくまで機能に関する知識が(習熟度は様々なれど)事前にある人が復習や反復学習的に見ることを想定した物です。
その意味では、むしろこの程度の内容で良いかもしれません。この記事だけで用法が明確にイメージできるメソッドであれば、実際の開発でも有効に使って行けるでしょう。その判断材料にもなる内容であると前向きに捉えておきましょう。

と言うことで、次回以降で各メソッドの機能を詳しく整理して行きたいと思います。