本文へ移動
Laravel Tips

コレクション

配列を便利に扱う入れ物「コレクション」の作り方と、全メソッドの一覧(取り出す・絞る・変える・並べる・集計する)、大量データ向けの遅延コレクションを説明します。

コレクションは、配列(値の並び)を便利に扱うための入れ物です。Illuminate\Support\Collection というクラスで、「並べかえる」「絞りこむ」「合計を出す」といった作業を、メソッド(クラスの中の関数)として用意してくれます。メソッドは続けて書けます。たとえば「名前を大文字にして、からっぽのものを取りのぞく」は、次のように1本の流れで書けます。

php
$collection = collect(['Taylor', 'Abigail', null])->map(function (?string $name) {
    return strtoupper($name);
})->reject(function (string $name) {
    return empty($name);
});

ふつうのコレクションは「元のものを書きかえない」のが基本です。メソッドを呼ぶと、新しいコレクションが返ってきます(元のコレクションはそのまま残ります)。ただし、例外のメソッドがいくつかあります(下の「元のコレクションを書きかえるメソッド」の表)。

コレクションを作る#

collect ヘルパー関数(どこからでも呼べる便利な関数)に配列を渡すと、コレクションができます。

php
$collection = collect([1, 2, 3]);

Collection::make や Collection::fromJson(JSON の文字列から作る)でも作れます。

補足

Eloquent(モデルを扱うしくみ)で取り出した結果は、いつも Collection として返ってきます。

メソッドを足す(マクロ)#

コレクションは「マクロ」に対応していて、自分で作ったメソッドを実行中に足せます。macro メソッドに、呼ばれたときに動かす関数(クロージャ)を渡します。関数の中では $this で、コレクションのほかのメソッドを呼べます。

php
use Illuminate\Support\Collection;
use Illuminate\Support\Str;

Collection::macro('toUpper', function () {
    return $this->map(function (string $value) {
        return Str::upper($value);
    });
});

$collection = collect(['first', 'second']);

$upper = $collection->toUpper();

// ['FIRST', 'SECOND']

マクロは、ふつうサービスプロバイダ(アプリの起動のときに、道具箱へ道具を登録する場所)の boot メソッドの中で登録します。

引数を受け取るマクロも作れます。

php
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Lang;

Collection::macro('toLocale', function (string $locale) {
    return $this->map(function (string $value) use ($locale) {
        return Lang::get($value, [], $locale);
    });
});

$collection = collect(['first', 'second']);

$translated = $collection->toLocale('es');

// ['primero', 'segundo'];

メソッド一覧を読む前に#

次の言葉は、一覧の中で何度も出てきます。

  • キーと値:連想配列(名前と値の組の並び)の「名前」がキー、中身が値です。['name' => 'Desk'] なら、name がキー、Desk が値です
  • ゆるい比較・きびしい比較:ゆるい比較では、"4"(文字列)と 4(数字)を同じものとみなします。きびしい比較(Strict)では、型もそろっていないと同じとみなしません。名前に Strict が付くメソッドが、きびしい比較の版です
  • 真偽テスト:値を受け取って true か false を返す関数のことです。「この条件に合うか」を決めるために渡します
  • エイリアス:同じ働きの別名です
  • 静的メソッド:Collection::make() のように、クラス名と :: で直接呼ぶメソッドです
  • 例外を投げる:「これ以上は進めない」というエラーの知らせを出して、処理を止めることです。ItemNotFoundException のような名前が、そのエラーの種類です
  • コンストラクタ:クラスからオブジェクトを作るときに、最初に動くメソッドです

メソッド一覧#

すべてのメソッドを、目的ごとの表にまとめます。あとの節に、よく使うものの例を載せます。

作る・変換して取り出す#

名前 説明
make 新しいコレクションを作る(静的メソッド。Collection::make([1, 2, 3]))
fromJson JSON の文字列を json_decode して、新しいコレクションを作る(静的メソッド)
wrap 値がコレクションや配列でなければ、包んでコレクションにする(静的メソッド)
unwrap コレクションなら、中の配列を取り出す。そうでなければそのまま返す(静的メソッド)
times 関数を指定の回数だけ呼び、その結果でコレクションを作る(静的メソッド)
range 指定の範囲の整数が入ったコレクションを作る
collect 同じ中身の、新しい Collection を返す。遅延コレクションをふつうのコレクションに変えるときに使う
lazy 同じ中身の遅延コレクション(LazyCollection)を返す
macro コレクションに自分のメソッドを足す(静的メソッド。上の「メソッドを足す」を参照)
all コレクションの中身の配列を、そのまま返す
toArray ふつうの配列に変える。中の Eloquent モデルなども配列に変わる
toJson JSON の文字列に変える
toPrettyJson 読みやすく整形した JSON の文字列に変える(JSON_PRETTY_PRINT)

数える・調べる#

名前 説明
count 要素の数を返す
countBy 値の出てきた回数を数える。関数を渡すと、その結果ごとに数える
isEmpty からっぽなら true
isNotEmpty からっぽでなければ true
hasMany 要素が2つ以上あれば true(条件の関数も渡せる)
hasSole 要素がちょうど1つなら true(条件の関数も渡せる)
contains 指定の値(または条件に合うもの)が入っているか調べる。ゆるい比較
containsStrict contains のきびしい比較の版
some contains の別名
doesntContain 指定の値(または条件に合うもの)が入っていないか調べる。ゆるい比較
doesntContainStrict doesntContain のきびしい比較の版
every すべての要素が条件に合うか調べる(からっぽなら true)
has 指定のキーがあるか調べる(配列で複数も渡せる)
hasAny 指定のキーのどれか1つでもあるか調べる
search 値を探して、そのキーを返す。見つからなければ false
ensure すべての要素が指定の型か確かめる。ちがえば UnexpectedValueException を投げる

1つ・いくつかを取り出す#

名前 説明
first 最初の要素(または条件に合う最初の要素)。無ければ null
firstOrFail first と同じ。見つからなければ ItemNotFoundException を投げる
firstWhere 指定のキーと値の組を持つ、最初の要素
last 最後の要素(または条件に合う最後の要素)。無ければ null
get 指定のキーの値。無ければ null(2番目の引数で初期値も指定できる)
value 最初の要素から、指定のキーの値を取り出す
sole 条件に合う要素が、ちょうど1つのときだけ、それを返す
random ランダムに1つ(または指定の数だけ)取り出す
after 指定の要素の、次の要素
before 指定の要素の、前の要素
nth n 番目ごとの要素を集めた、新しいコレクション
pluck 指定のキーの値だけを集める
keys キーだけを集める
values キーを 0 からの連番に振り直す

絞りこむ#

名前 説明
filter 条件に合う要素だけを残す。関数なしなら、false と同じ扱いの値を取りのぞく
reject 条件に合う要素を取りのぞく(filter の反対)
where 指定のキーの値が合う要素だけを残す。ゆるい比較で、比較の記号も使える
whereStrict where のきびしい比較の版
whereBetween 指定のキーの値が範囲の中にある要素だけを残す
whereNotBetween 指定のキーの値が範囲の外にある要素だけを残す
whereIn 指定のキーの値が、渡した配列の中にある要素だけを残す。ゆるい比較
whereInStrict whereIn のきびしい比較の版
whereNotIn 指定のキーの値が、渡した配列の中にない要素だけを残す。ゆるい比較
whereNotInStrict whereNotIn のきびしい比較の版
whereInstanceOf 指定のクラスの要素だけを残す
whereNull 指定のキーの値が null の要素だけを残す
whereNotNull 指定のキーの値が null でない要素だけを残す
only 指定のキーの要素だけを残す
except 指定のキーの要素を取りのぞく(only の反対)
select 指定のキーだけを選ぶ(SQL の SELECT のようなもの)
unique 重複をなくす。ゆるい比較
uniqueStrict unique のきびしい比較の版
duplicates 重複している値だけを取り出す。ゆるい比較
duplicatesStrict duplicates のきびしい比較の版
take 先頭から指定の数だけ取る(マイナスなら末尾から)
takeUntil 条件が true になる手前まで取る
takeWhile 条件が false になる手前まで取る
skip 先頭から指定の数だけ飛ばす
skipUntil 条件が true になるまで飛ばし、そこから残りを返す
skipWhile 条件が false になるまで飛ばし、そこから残りを返す
slice 指定の位置から切り出す(長さも指定できる)
forPage 「何ページ目・1ページに何件」で、そのページの要素を返す

集計する#

名前 説明
sum 合計
avg 平均
average avg の別名
max 最大値
min 最小値
median 中央値(並べたときの真ん中の値)
mode 最頻値(いちばん多く出てくる値)。配列で返る
percentage 条件に合う要素の割合(パーセント)

形を変える(元のコレクションは変わらない)#

名前 説明
map 要素ひとつずつを関数に通し、結果で新しいコレクションを作る
mapInto 要素をコンストラクタに渡して、指定のクラスのオブジェクトにする
mapSpread 入れ子の要素を、ばらして関数の引数に渡して変える
mapToGroups 関数が返した「キー => 値」で、グループに分ける
mapWithKeys 関数が返した「キー => 値」で、新しいキーと値の組を作る
flatMap map したあと、1段だけ平らにする
flatten 入れ子を平らにする(深さも指定できる)
flip キーと値を入れかえる
collapse 配列(コレクション)の入れ物を、1つの平らなコレクションにする
collapseWithKeys collapse と同じだが、元のキーを残す
dot 入れ子を「ドット記法」のキーで、1段にする
undot ドット記法のキーを、入れ子に戻す
zip 渡した配列の値を、同じ位置どうしで組にする
pad 指定の大きさになるまで、値で埋める
multiply 全要素のコピーを、指定の回数ぶん作る
reverse 順番を逆にする(キーは残る)
replace merge に似ているが、数字のキーも上書きする
replaceRecursive 入れ子の中まで replace する
keyBy 指定のキーの値を、新しいキーにする
groupBy 指定のキー(または関数の結果)でグループに分ける
partition 条件に合うものと合わないものの、2つに分ける
chunk 指定の大きさごとに分ける
chunkBy 隣どうしで同じ値のものを、まとめて分ける
chunkWhile 関数の結果にもとづいて、分ける
split 指定の数のグループに分ける
splitIn 指定の数のグループに、前から順に埋めて分ける
sliding 「すべり窓」のように、少しずつずらしながら分ける
implode 要素をつないで、1つの文字列にする
join 要素を文字列でつなぐ(最後だけつなぎ方を変えられる)

合わせる・比べる#

名前 説明
combine 要素をキーにして、別の配列の値と組にする
concat 渡した配列の値を、末尾に足す
merge 渡した配列を合わせる。同じ文字列のキーは、渡したほうで上書き
mergeRecursive 入れ子の中まで merge する。同じキーの値は配列にまとめる
union 渡した配列を足す。同じキーは、元のほうを優先
crossJoin すべての組み合わせ(直積)を作る
diff 元にあって、渡したものに無い「値」だけ残す
diffAssoc 元にあって、渡したものに無い「キーと値の組」だけ残す
diffAssocUsing diffAssoc で、キーの比べ方を関数で決める
diffKeys 元にあって、渡したものに無い「キー」の要素だけ残す
intersect 渡したものにも入っている「値」だけ残す
intersectUsing intersect で、値の比べ方を関数で決める
intersectAssoc 渡したものにも入っている「キーと値の組」だけ残す
intersectAssocUsing intersectAssoc で、キーの比べ方を関数で決める
intersectByKeys 渡したものにもある「キー」の要素だけ残す

並べかえる#

名前 説明
sort 値で並べかえる(キーは残る)
sortDesc 値で逆順に並べかえる(関数は渡せない)
sortBy 指定のキー(または関数の結果)で並べかえる
sortByDesc sortBy の逆順
sortKeys キーで並べかえる
sortKeysDesc キーで逆順に並べかえる
sortKeysUsing キーを、関数を使って並べかえる
shuffle ランダムに並べかえる

くり返す・値を1つにまとめる・つなぐ#

名前 説明
each 要素ひとつずつに関数を実行する(false を返すと止まる)
eachSpread 入れ子の要素をばらして関数に渡し、実行する
reduce 要素を1つの値にまとめる。前の結果を次に渡していく
reduceInto 渡した初期値を書きかえながら、1つにまとめる
reduceSpread 複数の初期値を持ちながら、配列にまとめる
pipe コレクションを関数に渡して、その結果を返す
pipeInto コレクションを、指定のクラスのコンストラクタに渡す
pipeThrough コレクションを、関数の配列に順に通す
tap 途中でコレクションを関数に渡す。コレクション自体は変わらない

条件で動かす#

名前 説明
when 最初の引数が true のとき、関数を実行する
unless 最初の引数が false のとき、関数を実行する
whenEmpty からっぽのとき、関数を実行する
whenNotEmpty からっぽでないとき、関数を実行する
unlessEmpty whenNotEmpty の別名
unlessNotEmpty whenEmpty の別名

元のコレクションを書きかえるメソッド#

名前 説明
forget 指定のキーの要素を消す(新しいコレクションは作らず、元のコレクションを書きかえて、それを返す)
pop 最後の要素を取り出して消す
shift 最初の要素を取り出して消す
prepend 先頭に要素を足す
push 末尾に要素を足す
put 指定のキーに値を入れる
pull 指定のキーの値を取り出して消す
splice 指定の位置から切り取って返す(かわりの要素も入れられる)
transform すべての要素を、関数の結果で置きかえる

注意

ほとんどのメソッドは新しいコレクションを返しますが、この表のメソッドは元のコレクションそのものを変えます。map ではなく transform を使うと、元が変わります。

中身を見る(デバッグ)#

名前 説明
dump 中身を画面に出す。処理はつづく
dd 中身を画面に出して、そこで処理を止める

よく使うメソッドの例#

map:1つずつ変える#

php
$collection = collect([1, 2, 3, 4, 5]);

$multiplied = $collection->map(function (int $item, int $key) {
    return $item * 2;
});

$multiplied->all();

// [2, 4, 6, 8, 10]

map は元のコレクションを変えません。元を変えたいときは transform を使います。

filter と reject:絞りこむ#

filter は、関数が true を返した要素だけを残します。

php
$collection = collect([1, 2, 3, 4]);

$filtered = $collection->filter(function (int $value, int $key) {
    return $value > 2;
});

$filtered->all();

// [3, 4]

関数を渡さないと、null・false・空文字・0・空の配列のように、false と同じ扱いのものが取りのぞかれます。

php
$collection = collect([1, 2, 3, null, false, '', 0, []]);

$collection->filter()->all();

// [1, 2, 3]

reject は逆に、true を返した要素を取りのぞきます。

php
$filtered = collect([1, 2, 3, 4])->reject(function (int $value, int $key) {
    return $value > 2;
});

$filtered->all();

// [1, 2]

where:キーの値で絞る#

php
$collection = collect([
    ['product' => 'Desk', 'price' => 200],
    ['product' => 'Chair', 'price' => 100],
    ['product' => 'Bookcase', 'price' => 150],
    ['product' => 'Door', 'price' => 100],
]);

$filtered = $collection->where('price', 100);

$filtered->all();

/*
    [
        ['product' => 'Chair', 'price' => 100],
        ['product' => 'Door', 'price' => 100],
    ]
*/

2番目の引数に、比較の記号を入れられます。使える記号は ===・!==・!=・==・=・<>・>・<・>=・<= です。

php
$collection = collect([
    ['name' => 'Jim', 'platform' => 'Mac'],
    ['name' => 'Sally', 'platform' => 'Mac'],
    ['name' => 'Sue', 'platform' => 'Linux'],
]);

$filtered = $collection->where('platform', '!=', 'Linux');

$filtered->all();

/*
    [
        ['name' => 'Jim', 'platform' => 'Mac'],
        ['name' => 'Sally', 'platform' => 'Mac'],
    ]
*/

範囲・含む・null で絞る where の仲間は、次のように使います。

php
$collection->whereBetween('price', [100, 200]);   // 100 以上 200 以下
$collection->whereNotBetween('price', [100, 200]); // 範囲の外
$collection->whereIn('price', [150, 200]);         // 150 か 200 のもの
$collection->whereNotIn('price', [150, 200]);      // 150 でも 200 でもないもの
$collection->whereNull('name');                    // name が null のもの
$collection->whereNotNull('name');                 // name が null でないもの
$collection->whereInstanceOf(User::class);         // User クラスのもの

補足

whereNotNull('name') は、null だけを取りのぞきます。0 や空文字は残ります。

pluck:キーの値を集める#

php
$collection = collect([
    ['product_id' => 'prod-100', 'name' => 'Desk'],
    ['product_id' => 'prod-200', 'name' => 'Chair'],
]);

$plucked = $collection->pluck('name');

$plucked->all();

// ['Desk', 'Chair']

2番目の引数で、結果のキーを決められます。

php
$plucked = $collection->pluck('name', 'product_id');

$plucked->all();

// ['prod-100' => 'Desk', 'prod-200' => 'Chair']

入れ子の値も、ドット記法(speakers.first_day のように . でつなぐ書き方)で取り出せます。

php
$collection = collect([
    [
        'name' => 'Laracon',
        'speakers' => [
            'first_day' => ['Rosa', 'Judith'],
        ],
    ],
    [
        'name' => 'VueConf',
        'speakers' => [
            'first_day' => ['Abigail', 'Joey'],
        ],
    ],
]);

$plucked = $collection->pluck('speakers.first_day');

$plucked->all();

// [['Rosa', 'Judith'], ['Abigail', 'Joey']]

キーが重なったときは、最後のものが残ります。

php
$collection = collect([
    ['brand' => 'Tesla',  'color' => 'red'],
    ['brand' => 'Pagani', 'color' => 'white'],
    ['brand' => 'Tesla',  'color' => 'black'],
    ['brand' => 'Pagani', 'color' => 'orange'],
]);

$plucked = $collection->pluck('color', 'brand');

$plucked->all();

// ['Tesla' => 'black', 'Pagani' => 'orange']

sum・avg・max・min・median・mode:集計#

php
collect([1, 2, 3, 4, 5])->sum();
// 15

collect([1, 1, 2, 4])->avg();
// 2

collect([1, 2, 3, 4, 5])->max();
// 5

collect([1, 2, 3, 4, 5])->min();
// 1

collect([1, 1, 2, 4])->median();
// 1.5

collect([1, 1, 2, 2])->mode();
// [1, 2]

配列の入った要素なら、キーを指定します。sum には関数も渡せます。

php
$collection = collect([
    ['name' => 'JavaScript: The Good Parts', 'pages' => 176],
    ['name' => 'JavaScript: The Definitive Guide', 'pages' => 1096],
]);

$collection->sum('pages');

// 1272
php
$collection = collect([
    ['name' => 'Chair', 'colors' => ['Black']],
    ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']],
    ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']],
]);

$collection->sum(function (array $product) {
    return count($product['colors']);
});

// 6

percentage は、条件に合う要素の割合を出します。小数点以下は、はじめは2桁までです。precision で変えられます。

php
$collection = collect([1, 1, 2, 2, 2, 3]);

$percentage = $collection->percentage(fn (int $value) => $value === 1);

// 33.33

$percentage = $collection->percentage(fn (int $value) => $value === 1, precision: 3);

// 33.333

first・last・get:取り出す#

php
collect([1, 2, 3, 4])->first(function (int $value, int $key) {
    return $value > 2;
});
// 3

collect([1, 2, 3, 4])->first();
// 1

collect([1, 2, 3, 4])->last();
// 4

get は、キーを指定して値を取り出します。無いときの値(関数も可)を2番目に渡せます。

php
$collection = collect(['name' => 'Taylor', 'framework' => 'Laravel']);

$collection->get('name');
// Taylor

$collection->get('age', 34);
// 34

$collection->get('email', function () {
    return 'taylor@example.com';
});
// taylor@example.com

firstWhere は、キーと値の組で探します。

php
$collection = collect([
    ['name' => 'Regena', 'age' => null],
    ['name' => 'Linda', 'age' => 14],
    ['name' => 'Diego', 'age' => 23],
    ['name' => 'Linda', 'age' => 84],
]);

$collection->firstWhere('name', 'Linda');
// ['name' => 'Linda', 'age' => 14]

$collection->firstWhere('age', '>=', 18);
// ['name' => 'Diego', 'age' => 23]

$collection->firstWhere('age');
// ['name' => 'Linda', 'age' => 14](age が真と同じ扱いになる最初のもの)

sole は、条件に合うものが「ちょうど1つ」のときだけ返します。1つも無ければ ItemNotFoundException、2つ以上あれば MultipleItemsFoundException が投げられます。

php
collect([1, 2, 3, 4])->sole(function (int $value, int $key) {
    return $value === 2;
});
// 2

$collection = collect([
    ['product' => 'Desk', 'price' => 200],
    ['product' => 'Chair', 'price' => 100],
]);

$collection->sole('product', 'Chair');
// ['product' => 'Chair', 'price' => 100]

contains・every・search・has:調べる#

php
$collection = collect([1, 2, 3, 4, 5]);

$collection->contains(function (int $value, int $key) {
    return $value > 5;
});
// false

$collection = collect(['name' => 'Desk', 'price' => 100]);

$collection->contains('Desk');
// true

$collection = collect([
    ['product' => 'Desk', 'price' => 200],
    ['product' => 'Chair', 'price' => 100],
]);

$collection->contains('product', 'Bookcase');
// false
php
collect([1, 2, 3, 4])->every(function (int $value, int $key) {
    return $value > 2;
});
// false

collect([2, 4, 6, 8])->search(4);
// 1

collect([2, 4, 6, 8])->search('4', strict: true);
// false

collect([2, 4, 6, 8])->search(function (int $item, int $key) {
    return $item > 5;
});
// 2
php
$collection = collect(['account_id' => 1, 'product' => 'Desk', 'amount' => 5]);

$collection->has('product');
// true

$collection->has(['product', 'amount']);
// true

$collection->has(['amount', 'price']);
// false

$collection->hasAny(['product', 'price']);
// true

after と before も、strict: true や関数を渡せます。

php
$collection = collect([1, 2, 3, 4, 5]);

$collection->after(3);
// 4

$collection->after(5);
// null

$collection->before(3);
// 2

collect([2, 4, 6, 8])->after('4', strict: true);
// null

collect([2, 4, 6, 8])->after(function (int $item, int $key) {
    return $item > 5;
});
// 8

ensure は、型がそろっているかの確認に使います。クラス名や、string・int・float・bool・array のような基本の型も指定できます。

php
return $collection->ensure(User::class);

return $collection->ensure([User::class, Customer::class]);

return $collection->ensure('int');

注意

ensure は、あとから別の型の要素が足されないことまでは守ってくれません。

groupBy・keyBy・partition:分ける#

php
$collection = collect([
    ['account_id' => 'account-x10', 'product' => 'Chair'],
    ['account_id' => 'account-x10', 'product' => 'Bookcase'],
    ['account_id' => 'account-x11', 'product' => 'Desk'],
]);

$grouped = $collection->groupBy('account_id');

$grouped->all();

/*
    [
        'account-x10' => [
            ['account_id' => 'account-x10', 'product' => 'Chair'],
            ['account_id' => 'account-x10', 'product' => 'Bookcase'],
        ],
        'account-x11' => [
            ['account_id' => 'account-x11', 'product' => 'Desk'],
        ],
    ]
*/

キーの代わりに、関数を渡すこともできます。

php
$grouped = $collection->groupBy(function (array $item, int $key) {
    return substr($item['account_id'], -3);
});

$grouped->all();

/*
    [
        'x10' => [
            ['account_id' => 'account-x10', 'product' => 'Chair'],
            ['account_id' => 'account-x10', 'product' => 'Bookcase'],
        ],
        'x11' => [
            ['account_id' => 'account-x11', 'product' => 'Desk'],
        ],
    ]
*/

配列で複数の条件を渡すと、段ごとに入れ子で分けます。preserveKeys: true で、元のキーを残せます。

php
$data = new Collection([
    10 => ['user' => 1, 'skill' => 1, 'roles' => ['Role_1', 'Role_3']],
    20 => ['user' => 2, 'skill' => 1, 'roles' => ['Role_1', 'Role_2']],
    30 => ['user' => 3, 'skill' => 2, 'roles' => ['Role_1']],
    40 => ['user' => 4, 'skill' => 2, 'roles' => ['Role_2']],
]);

$result = $data->groupBy(['skill', function (array $item) {
    return $item['roles'];
}], preserveKeys: true);

/*
[
    1 => [
        'Role_1' => [
            10 => ['user' => 1, 'skill' => 1, 'roles' => ['Role_1', 'Role_3']],
            20 => ['user' => 2, 'skill' => 1, 'roles' => ['Role_1', 'Role_2']],
        ],
        'Role_3' => [
            10 => ['user' => 1, 'skill' => 1, 'roles' => ['Role_1', 'Role_3']],
        ],
        'Role_2' => [
            20 => ['user' => 2, 'skill' => 1, 'roles' => ['Role_1', 'Role_2']],
        ],
    ],
    2 => [
        'Role_1' => [
            30 => ['user' => 3, 'skill' => 2, 'roles' => ['Role_1']],
        ],
        'Role_2' => [
            40 => ['user' => 4, 'skill' => 2, 'roles' => ['Role_2']],
        ],
    ],
];
*/

keyBy は、指定のキーの値を、新しいキーにします。同じキーが複数あるときは、最後のものだけが残ります。

php
$collection = collect([
    ['product_id' => 'prod-100', 'name' => 'Desk'],
    ['product_id' => 'prod-200', 'name' => 'Chair'],
]);

$keyed = $collection->keyBy('product_id');

$keyed->all();

/*
    [
        'prod-100' => ['product_id' => 'prod-100', 'name' => 'Desk'],
        'prod-200' => ['product_id' => 'prod-200', 'name' => 'Chair'],
    ]
*/

$keyed = $collection->keyBy(function (array $item, int $key) {
    return strtoupper($item['product_id']);
});

/*
    [
        'PROD-100' => ['product_id' => 'prod-100', 'name' => 'Desk'],
        'PROD-200' => ['product_id' => 'prod-200', 'name' => 'Chair'],
    ]
*/

partition は、PHP の配列の分解代入([$a, $b] = ... の書き方)と合わせて使います。

php
$collection = collect([1, 2, 3, 4, 5, 6]);

[$underThree, $equalOrAboveThree] = $collection->partition(function (int $i) {
    return $i < 3;
});

$underThree->all();

// [1, 2]

$equalOrAboveThree->all();

// [3, 4, 5, 6]

mapToGroups と mapWithKeys は、関数が「キー => 値」を1組ずつ返します。

php
$collection = collect([
    ['name' => 'John Doe', 'department' => 'Sales'],
    ['name' => 'Jane Doe', 'department' => 'Sales'],
    ['name' => 'Johnny Doe', 'department' => 'Marketing'],
]);

$grouped = $collection->mapToGroups(function (array $item, int $key) {
    return [$item['department'] => $item['name']];
});

$grouped->all();

/*
    [
        'Sales' => ['John Doe', 'Jane Doe'],
        'Marketing' => ['Johnny Doe'],
    ]
*/
php
$collection = collect([
    ['name' => 'John', 'department' => 'Sales', 'email' => 'john@example.com'],
    ['name' => 'Jane', 'department' => 'Marketing', 'email' => 'jane@example.com'],
]);

$keyed = $collection->mapWithKeys(function (array $item, int $key) {
    return [$item['email'] => $item['name']];
});

$keyed->all();

/*
    [
        'john@example.com' => 'John',
        'jane@example.com' => 'Jane',
    ]
*/

chunk・split・sliding:小分けにする#

php
$collection = collect([1, 2, 3, 4, 5, 6, 7]);

$chunks = $collection->chunk(4);

$chunks->all();

// [[1, 2, 3, 4], [5, 6, 7]]

画面(ビュー)で、商品を3つずつ横に並べたいときなどに便利です。

blade
@foreach ($products->chunk(3) as $chunk)
    <div class="row">
        @foreach ($chunk as $product)
            <div class="col-xs-4">{{ $product->name }}</div>
        @endforeach
    </div>
@endforeach

chunkBy は、隣どうしで同じ値のものをまとめます。groupBy とちがい、同じ値でも離れていれば別の塊になります。

php
$chunks = $products->chunkBy('parent');

$collection = collect([1, 1, 2, 2, 1]);

$chunks = $collection->chunkBy(fn (int $value) => $value);

$chunks->all();

// [[1, 1], [2, 2], [1]]

chunkWhile は、関数の結果で分けます。関数には、いまの塊($chunk)も渡されるので、1つ前の要素と比べられます。

php
$collection = collect(str_split('AABBCCCD'));

$chunks = $collection->chunkWhile(function (string $value, int $key, Collection $chunk) {
    return $value === $chunk->last();
});

$chunks->all();

// [['A', 'A'], ['B', 'B'], ['C', 'C', 'C'], ['D']]

split と splitIn は、グループの数を指定して分けます。

php
$collection = collect([1, 2, 3, 4, 5]);

$groups = $collection->split(3);

$groups->all();

// [[1, 2], [3, 4], [5]]

$collection = collect([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);

$groups = $collection->splitIn(3);

$groups->all();

// [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]

sliding は、決まった数ずつ、少しずつずらしながら切り出します。となりの塊と重なる部分ができます。2番目の step で、ずらす幅を変えられます。

php
$collection = collect([1, 2, 3, 4, 5]);

$chunks = $collection->sliding(2);

$chunks->toArray();

// [[1, 2], [2, 3], [3, 4], [4, 5]]

$chunks = $collection->sliding(3, step: 2);

$chunks->toArray();

// [[1, 2, 3], [3, 4, 5]]

eachSpread と組み合わせると、「ひとつ前」と比べる処理が書けます。

php
$transactions->sliding(2)->eachSpread(function ($previous, $current) {
    $current->total = $previous->total + $current->amount;
});

take・skip・slice・forPage:一部を切り出す#

php
$collection = collect([0, 1, 2, 3, 4, 5]);

$collection->take(3)->all();
// [0, 1, 2]

$collection->take(-2)->all();
// [4, 5]

$collection = collect([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);

$collection->skip(4)->all();
// [5, 6, 7, 8, 9, 10]

$collection->slice(4)->all();
// [5, 6, 7, 8, 9, 10]

$collection->slice(4, 2)->all();
// [5, 6]

collect([1, 2, 3, 4, 5, 6, 7, 8, 9])->forPage(2, 3)->all();
// [4, 5, 6]

slice はキーを残します。連番に振り直したいときは values を使います。

takeUntil・takeWhile・skipUntil・skipWhile は、関数(takeUntil と skipUntil は単純な値も可)で境目を決めます。

php
$collection = collect([1, 2, 3, 4]);

$collection->takeUntil(function (int $item) {
    return $item >= 3;
})->all();
// [1, 2]

$collection->takeUntil(3)->all();
// [1, 2]

$collection->takeWhile(function (int $item) {
    return $item < 3;
})->all();
// [1, 2]

$collection->skipUntil(function (int $item) {
    return $item >= 3;
})->all();
// [3, 4]

$collection->skipUntil(3)->all();
// [3, 4]

$collection->skipWhile(function (int $item) {
    return $item <= 3;
})->all();
// [4]

注意

値が見つからない、または条件がずっと変わらないときの結果に気をつけます。takeUntil と takeWhile は全部の要素を返し、skipUntil と skipWhile は空のコレクションを返します。

sort と sortBy:並べかえる#

sort はキーを残したまま並べかえるので、連番に振り直すには values を続けます。

php
$collection = collect([5, 3, 1, 2, 4]);

$sorted = $collection->sort();

$sorted->values()->all();

// [1, 2, 3, 4, 5]

もっと細かい並べかたは、sort に自分の比べる関数を渡します(PHP の uasort と同じ使い方です)。入れ子の配列やオブジェクトは sortBy と sortByDesc を使います。

php
$collection = collect([
    ['name' => 'Desk', 'price' => 200],
    ['name' => 'Chair', 'price' => 100],
    ['name' => 'Bookcase', 'price' => 150],
]);

$sorted = $collection->sortBy('price');

$sorted->values()->all();

/*
    [
        ['name' => 'Chair', 'price' => 100],
        ['name' => 'Bookcase', 'price' => 150],
        ['name' => 'Desk', 'price' => 200],
    ]
*/

2番目の引数に、PHP の並べかえのフラグ(並べかたの種類を決める印。下の例の SORT_NATURAL など)を渡せます。

php
$collection = collect([
    ['title' => 'Item 1'],
    ['title' => 'Item 12'],
    ['title' => 'Item 3'],
]);

$sorted = $collection->sortBy('title', SORT_NATURAL);

$sorted->values()->all();

/*
    [
        ['title' => 'Item 1'],
        ['title' => 'Item 3'],
        ['title' => 'Item 12'],
    ]
*/

関数で並べかたを決めることもできます。

php
$collection = collect([
    ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']],
    ['name' => 'Chair', 'colors' => ['Black']],
    ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']],
]);

$sorted = $collection->sortBy(function (array $product, int $key) {
    return count($product['colors']);
});

$sorted->values()->all();

/*
    [
        ['name' => 'Chair', 'colors' => ['Black']],
        ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']],
        ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']],
    ]
*/

複数の条件で並べるときは、「キーと向き」の組を配列で渡します。

php
$collection = collect([
    ['name' => 'Taylor Otwell', 'age' => 34],
    ['name' => 'Abigail Otwell', 'age' => 30],
    ['name' => 'Taylor Otwell', 'age' => 36],
    ['name' => 'Abigail Otwell', 'age' => 32],
]);

$sorted = $collection->sortBy([
    ['name', 'asc'],
    ['age', 'desc'],
]);

$sorted->values()->all();

/*
    [
        ['name' => 'Abigail Otwell', 'age' => 32],
        ['name' => 'Abigail Otwell', 'age' => 30],
        ['name' => 'Taylor Otwell', 'age' => 36],
        ['name' => 'Taylor Otwell', 'age' => 34],
    ]
*/

条件ごとに、比べる関数を渡すこともできます。

php
$sorted = $collection->sortBy([
    fn (array $a, array $b) => $a['name'] <=> $b['name'],
    fn (array $a, array $b) => $b['age'] <=> $a['age'],
]);

$sorted->values()->all();

/*
    [
        ['name' => 'Abigail Otwell', 'age' => 32],
        ['name' => 'Abigail Otwell', 'age' => 30],
        ['name' => 'Taylor Otwell', 'age' => 36],
        ['name' => 'Taylor Otwell', 'age' => 34],
    ]
*/

sortDesc は sort の逆順です。sort とちがい、関数は渡せません。逆順にしたい関数があるなら、sort に渡して比べかたを逆にします。

php
$collection = collect([5, 3, 1, 2, 4]);

$sorted = $collection->sortDesc();

$sorted->values()->all();

// [5, 4, 3, 2, 1]

キーで並べるのは sortKeys です。

php
$collection = collect([
    'id' => 22345,
    'first' => 'John',
    'last' => 'Doe',
]);

$sorted = $collection->sortKeys();

$sorted->all();

/*
    [
        'first' => 'John',
        'id' => 22345,
        'last' => 'Doe',
    ]
*/

sortKeysDesc は逆順です。sortKeysUsing は、比べる関数を渡します(PHP の uksort と同じ使い方で、関数は 0 より小さい・0・0 より大きい整数を返します)。

php
$collection = collect([
    'ID' => 22345,
    'first' => 'John',
    'last' => 'Doe',
]);

$sorted = $collection->sortKeysUsing('strnatcasecmp');

$sorted->all();

/*
    [
        'first' => 'John',
        'ID' => 22345,
        'last' => 'Doe',
    ]
*/

flatten・collapse・dot:平らにする・入れ子にする#

php
$collection = collect([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]);

$collection->collapse()->all();

// [1, 2, 3, 4, 5, 6, 7, 8, 9]
php
$collection = collect([
    ['first'  => collect([1, 2, 3])],
    ['second' => [4, 5, 6]],
    ['third'  => collect([7, 8, 9])]
]);

$collection->collapseWithKeys()->all();

// [
//     'first'  => [1, 2, 3],
//     'second' => [4, 5, 6],
//     'third'  => [7, 8, 9],
// ]

collapseWithKeys は、すでに平らなコレクションに使うと、空のコレクションを返します。

php
$collection = collect([
    'name' => 'Taylor',
    'languages' => [
        'PHP', 'JavaScript'
    ]
]);

$collection->flatten()->all();

// ['Taylor', 'PHP', 'JavaScript'];

flatten に深さを渡すと、その段数だけ平らにします。

php
$collection = collect([
    'Apple' => [
        [
            'name' => 'iPhone 6S',
            'brand' => 'Apple'
        ],
    ],
    'Samsung' => [
        [
            'name' => 'Galaxy S7',
            'brand' => 'Samsung'
        ],
    ],
]);

$products = $collection->flatten(1);

$products->values()->all();

/*
    [
        ['name' => 'iPhone 6S', 'brand' => 'Apple'],
        ['name' => 'Galaxy S7', 'brand' => 'Samsung'],
    ]
*/

深さを渡さずに flatten すると、この例は ['iPhone 6S', 'Apple', 'Galaxy S7', 'Samsung'] まで平らになります。

flatMap は、map のあとに1段だけ平らにします。

php
$collection = collect([
    ['name' => 'Sally'],
    ['school' => 'Arkansas'],
    ['age' => 28]
]);

$flattened = $collection->flatMap(function (array $values) {
    return array_map('strtoupper', $values);
});

$flattened->all();

// ['name' => 'SALLY', 'school' => 'ARKANSAS', 'age' => '28'];

dot と undot は、入れ子とドット記法を行き来します。

php
$collection = collect(['products' => ['desk' => ['price' => 100]]]);

$collection->dot()->all();

// ['products.desk.price' => 100]
php
$person = collect([
    'name.first_name' => 'Marie',
    'name.last_name' => 'Valentine',
    'address.line_1' => '2992 Eagle Drive',
    'address.line_2' => '',
    'address.suburb' => 'Detroit',
    'address.state' => 'MI',
    'address.postcode' => '48219'
]);

$person = $person->undot();

$person->toArray();

/*
    [
        "name" => [
            "first_name" => "Marie",
            "last_name" => "Valentine",
        ],
        "address" => [
            "line_1" => "2992 Eagle Drive",
            "line_2" => "",
            "suburb" => "Detroit",
            "state" => "MI",
            "postcode" => "48219",
        ],
    ]
*/

合わせる#

php
// combine:要素をキーにして、値と組にする
collect(['name', 'age'])->combine(['George', 29])->all();
// ['name' => 'George', 'age' => 29]

// concat:末尾に足す(キーは数字の連番に振り直される)
collect(['John Doe'])->concat(['Jane Doe'])->concat(['name' => 'Johnny Doe'])->all();
// ['John Doe', 'Jane Doe', 'Johnny Doe']

// merge:同じ文字列のキーは、渡したほうで上書き
collect(['product_id' => 1, 'price' => 100])->merge(['price' => 200, 'discount' => false])->all();
// ['product_id' => 1, 'price' => 200, 'discount' => false]

// merge:数字のキーは、末尾に足される
collect(['Desk', 'Chair'])->merge(['Bookcase', 'Door'])->all();
// ['Desk', 'Chair', 'Bookcase', 'Door']

// mergeRecursive:同じ文字列のキーの値は、配列にまとまる
collect(['product_id' => 1, 'price' => 100])->mergeRecursive([
    'product_id' => 2,
    'price' => 200,
    'discount' => false
])->all();
// ['product_id' => [1, 2], 'price' => [100, 200], 'discount' => false]

// union:同じキーは、元のほうを優先
collect([1 => ['a'], 2 => ['b']])->union([3 => ['c'], 1 => ['d']])->all();
// [1 => ['a'], 2 => ['b'], 3 => ['c']]

// replace:数字のキーも上書きする
collect(['Taylor', 'Abigail', 'James'])->replace([1 => 'Victoria', 3 => 'Finn'])->all();
// ['Taylor', 'Victoria', 'James', 'Finn']

// zip:同じ位置どうしを組にする
collect(['Chair', 'Desk'])->zip([100, 200])->all();
// [['Chair', 100], ['Desk', 200]]

concat はキーを連番に振り直すので、文字列のキーを残したいときは merge を使います。

replaceRecursive は、入れ子の中でも置きかえます。

php
$collection = collect([
    'Taylor',
    'Abigail',
    [
        'James',
        'Victoria',
        'Finn'
    ]
]);

$replaced = $collection->replaceRecursive([
    'Charlie',
    2 => [1 => 'King']
]);

$replaced->all();

// ['Charlie', 'Abigail', ['James', 'King', 'Finn']]

crossJoin は、すべての組み合わせを作ります。

php
$collection = collect([1, 2]);

$matrix = $collection->crossJoin(['a', 'b']);

$matrix->all();

/*
    [
        [1, 'a'],
        [1, 'b'],
        [2, 'a'],
        [2, 'b'],
    ]
*/

$matrix = $collection->crossJoin(['a', 'b'], ['I', 'II']);

$matrix->all();

/*
    [
        [1, 'a', 'I'],
        [1, 'a', 'II'],
        [1, 'b', 'I'],
        [1, 'b', 'II'],
        [2, 'a', 'I'],
        [2, 'a', 'II'],
        [2, 'b', 'I'],
        [2, 'b', 'II'],
    ]
*/

違いと共通部分#

php
// diff:元にあって、渡したものに無い値
collect([1, 2, 3, 4, 5])->diff([2, 4, 6, 8])->all();
// [1, 3, 5]

// diffAssoc:キーと値の組で比べる
collect(['color' => 'orange', 'type' => 'fruit', 'remain' => 6])->diffAssoc([
    'color' => 'yellow',
    'type' => 'fruit',
    'remain' => 3,
    'used' => 6,
])->all();
// ['color' => 'orange', 'remain' => 6]

// diffKeys:キーで比べる
collect([
    'one' => 10,
    'two' => 20,
    'three' => 30,
    'four' => 40,
    'five' => 50,
])->diffKeys([
    'two' => 2,
    'four' => 4,
    'six' => 6,
    'eight' => 8,
])->all();
// ['one' => 10, 'three' => 30, 'five' => 50]

// intersect:渡したものにもある値(キーは残る)
collect(['Desk', 'Sofa', 'Chair'])->intersect(['Desk', 'Chair', 'Bookcase'])->all();
// [0 => 'Desk', 2 => 'Chair']

// intersectAssoc:キーと値の組が同じもの
collect(['color' => 'red', 'size' => 'M', 'material' => 'cotton'])->intersectAssoc([
    'color' => 'blue',
    'size' => 'M',
    'material' => 'polyester'
])->all();
// ['size' => 'M']

// intersectByKeys:キーが同じもの
collect(['serial' => 'UX301', 'type' => 'screen', 'year' => 2009])->intersectByKeys([
    'reference' => 'UX404', 'type' => 'tab', 'year' => 2011,
])->all();
// ['type' => 'screen', 'year' => 2009]

diffAssocUsing・intersectUsing・intersectAssocUsing は、比べる関数を自分で渡します。関数は、0 より小さい・0・0 より大きい整数を返します(diffAssocUsing は、PHP の array_diff_uassoc を使っています)。

php
$collection = collect([
    'color' => 'orange',
    'type' => 'fruit',
    'remain' => 6,
]);

$diff = $collection->diffAssocUsing([
    'Color' => 'yellow',
    'Type' => 'fruit',
    'Remain' => 3,
], 'strnatcasecmp');

$diff->all();

// ['color' => 'orange', 'remain' => 6]
php
$collection = collect(['Desk', 'Sofa', 'Chair']);

$intersect = $collection->intersectUsing(['desk', 'chair', 'bookcase'], function (string $a, string $b) {
    return strcasecmp($a, $b);
});

$intersect->all();

// [0 => 'Desk', 2 => 'Chair']
php
$collection = collect([
    'color' => 'red',
    'Size' => 'M',
    'material' => 'cotton',
]);

$intersect = $collection->intersectAssocUsing([
    'color' => 'blue',
    'size' => 'M',
    'material' => 'polyester',
], function (string $a, string $b) {
    return strcasecmp($a, $b);
});

$intersect->all();

// ['Size' => 'M']

unique と duplicates:重複#

php
$collection = collect([1, 1, 2, 2, 3, 4, 2]);

$collection->unique()->values()->all();

// [1, 2, 3, 4]

collect(['a', 'b', 'a', 'c', 'b'])->duplicates();

// [2 => 'a', 4 => 'b']

入れ子の配列やオブジェクトには、キーや関数を渡せます。

php
$collection = collect([
    ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'],
    ['name' => 'iPhone 5', 'brand' => 'Apple', 'type' => 'phone'],
    ['name' => 'Apple Watch', 'brand' => 'Apple', 'type' => 'watch'],
    ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'],
    ['name' => 'Galaxy Gear', 'brand' => 'Samsung', 'type' => 'watch'],
]);

$unique = $collection->unique('brand');

$unique->values()->all();

/*
    [
        ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'],
        ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'],
    ]
*/

$unique = $collection->unique(function (array $item) {
    return $item['brand'].$item['type'];
});

$unique->values()->all();

/*
    [
        ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'],
        ['name' => 'Apple Watch', 'brand' => 'Apple', 'type' => 'watch'],
        ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'],
        ['name' => 'Galaxy Gear', 'brand' => 'Samsung', 'type' => 'watch'],
    ]
*/
php
$employees = collect([
    ['email' => 'abigail@example.com', 'position' => 'Developer'],
    ['email' => 'james@example.com', 'position' => 'Designer'],
    ['email' => 'victoria@example.com', 'position' => 'Developer'],
]);

$employees->duplicates('position');

// [2 => 'Developer']

each・reduce・pipe・tap:くり返す・まとめる#

php
$collection = collect([1, 2, 3, 4]);

$collection->each(function (int $item, int $key) {
    // ...
});

// 途中で止めたいときは false を返す
$collection->each(function (int $item, int $key) {
    if (/* condition */) {
        return false;
    }
});

eachSpread は、入れ子の値をばらして関数に渡します。mapSpread も同じ形で、結果から新しいコレクションを作ります。

php
$collection = collect([['John Doe', 35], ['Jane Doe', 33]]);

$collection->eachSpread(function (string $name, int $age) {
    // ...
});

$collection = collect([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);

$chunks = $collection->chunk(2);

$sequence = $chunks->mapSpread(function (int $even, int $odd) {
    return $even + $odd;
});

$sequence->all();

// [1, 5, 9, 13, 17]

reduce は、前の結果を次に渡しながら、1つの値にまとめます。1回目の $carry は null で、2番目の引数で初期値を決められます。

php
$collection = collect([1, 2, 3]);

$total = $collection->reduce(function (?int $carry, int $item) {
    return $carry + $item;
});

// 6

$collection->reduce(function (int $carry, int $item) {
    return $carry + $item;
}, 4);

// 10

キーも関数に渡されます。

php
$collection = collect([
    'usd' => 1400,
    'gbp' => 1200,
    'eur' => 1000,
]);

$ratio = [
    'usd' => 1,
    'gbp' => 1.37,
    'eur' => 1.22,
];

$collection->reduce(function (int $carry, int $value, string $key) use ($ratio) {
    return $carry + ($value * $ratio[$key]);
}, 0);

// 4264

reduceInto は、渡した初期値を書きかえていくやり方です。reduce とちがい、関数は結果を返さなくてかまいません。

php
class OrderStats
{
    public int $total = 0;

    public int $count = 0;
}

$orders = collect([
    ['amount' => 100],
    ['amount' => 250],
    ['amount' => 50],
]);

$stats = $orders->reduceInto(new OrderStats, function (OrderStats $stats, array $order) {
    $stats->total += $order['amount'];
    $stats->count++;
});

$stats->total;

// 400

数字や配列にまとめるときは、関数が書きかえた結果が元に反映されるよう、参照(&。コピーではなく、元の変数そのものを受け取る書き方)で受け取ります。

php
$collection = collect([1, 2, 3, 4, 5]);

$even = $collection->reduceInto([], function (array &$result, int $value) {
    if ($value % 2 === 0) {
        $result[] = $value;
    }
});

// [2, 4]

reduceSpread は、複数の初期値を持てる reduce です。

php
[$creditsRemaining, $batch] = Image::where('status', 'unprocessed')
    ->get()
    ->reduceSpread(function (int $creditsRemaining, Collection $batch, Image $image) {
        if ($creditsRemaining >= $image->creditsRequired()) {
            $batch->push($image);

            $creditsRemaining -= $image->creditsRequired();
        }

        return [$creditsRemaining, $batch];
    }, $creditsAvailable, collect());

pipe・pipeInto・pipeThrough は、コレクション全体を受け渡します。

php
$collection = collect([1, 2, 3]);

$piped = $collection->pipe(function (Collection $collection) {
    return $collection->sum();
});

// 6
php
class ResourceCollection
{
    /**
     * Create a new ResourceCollection instance.
     */
    public function __construct(
        public Collection $collection,
    ) {}
}

$collection = collect([1, 2, 3]);

$resource = $collection->pipeInto(ResourceCollection::class);

$resource->collection->all();

// [1, 2, 3]
php
use Illuminate\Support\Collection;

$collection = collect([1, 2, 3]);

$result = $collection->pipeThrough([
    function (Collection $collection) {
        return $collection->merge([4, 5]);
    },
    function (Collection $collection) {
        return $collection->sum();
    },
]);

// 15

tap は、流れの途中でコレクションをのぞき見るときに使います。コレクション自体は変わりません。

php
collect([2, 4, 3, 1, 5])
    ->sort()
    ->tap(function (Collection $collection) {
        Log::debug('Values after sorting', $collection->values()->all());
    })
    ->shift();

// 1

mapInto は、値をコンストラクタに渡して、クラスのオブジェクトにします。

php
class Currency
{
    /**
     * Create a new currency instance.
     */
    function __construct(
        public string $code,
    ) {}
}

$collection = collect(['USD', 'EUR', 'GBP']);

$currencies = $collection->mapInto(Currency::class);

$currencies->all();

// [Currency('USD'), Currency('EUR'), Currency('GBP')]

when・unless:条件で動かす#

when は、最初の引数が true のときだけ関数を実行します。関数には、コレクションと最初の引数が渡されます。2番目の関数は、false のときに実行されます。

php
$collection = collect([1, 2, 3]);

$collection->when(true, function (Collection $collection, bool $value) {
    return $collection->push(4);
});

$collection->when(false, function (Collection $collection, bool $value) {
    return $collection->push(5);
});

$collection->all();

// [1, 2, 3, 4]
php
$collection = collect([1, 2, 3]);

$collection->when(false, function (Collection $collection, bool $value) {
    return $collection->push(4);
}, function (Collection $collection, bool $value) {
    return $collection->push(5);
});

$collection->all();

// [1, 2, 3, 5]

unless は逆で、最初の引数が false のときに関数を実行します(2番目の関数は true のとき)。

php
$collection = collect([1, 2, 3]);

$collection->unless(true, function (Collection $collection, bool $value) {
    return $collection->push(4);
});

$collection->unless(false, function (Collection $collection, bool $value) {
    return $collection->push(5);
});

$collection->all();

// [1, 2, 3, 5]

whenEmpty と whenNotEmpty は、からっぽかどうかで関数を実行します。2番目の関数は、条件が合わなかったときに実行されます。

php
$collection = collect(['Michael', 'Tom']);

$collection->whenEmpty(function (Collection $collection) {
    return $collection->push('Adam');
});

$collection->all();

// ['Michael', 'Tom']

$collection = collect();

$collection->whenEmpty(function (Collection $collection) {
    return $collection->push('Adam');
});

$collection->all();

// ['Adam']
php
$collection = collect(['Michael', 'Tom']);

$collection->whenEmpty(function (Collection $collection) {
    return $collection->push('Adam');
}, function (Collection $collection) {
    return $collection->push('Taylor');
});

$collection->all();

// ['Michael', 'Tom', 'Taylor']
php
$collection = collect(['Michael', 'Tom']);

$collection->whenNotEmpty(function (Collection $collection) {
    return $collection->push('Adam');
});

$collection->all();

// ['Michael', 'Tom', 'Adam']

$collection = collect();

$collection->whenNotEmpty(function (Collection $collection) {
    return $collection->push('Adam');
}, function (Collection $collection) {
    return $collection->push('Taylor');
});

$collection->all();

// ['Taylor']

足す・消す(元のコレクションが変わる)#

php
$collection = collect([1, 2, 3, 4, 5]);

$collection->pop();
// 5(collection は [1, 2, 3, 4] になる)

$collection = collect([1, 2, 3, 4, 5]);

$collection->pop(3);
// collect([5, 4, 3])(collection は [1, 2] になる)

$collection = collect([1, 2, 3, 4, 5]);

$collection->shift();
// 1(collection は [2, 3, 4, 5] になる)

$collection = collect([1, 2, 3, 4, 5]);

$collection->shift(3);
// collect([1, 2, 3])(collection は [4, 5] になる)

$collection = collect([1, 2, 3, 4, 5]);

$collection->prepend(0);
// [0, 1, 2, 3, 4, 5]

$collection = collect(['one' => 1, 'two' => 2]);

$collection->prepend(0, 'zero');
// ['zero' => 0, 'one' => 1, 'two' => 2]

$collection = collect([1, 2, 3, 4]);

$collection->push(5);
// [1, 2, 3, 4, 5]

$collection->push(6, 7);
// [1, 2, 3, 4, 5, 6, 7]

$collection = collect(['product_id' => 1, 'name' => 'Desk']);

$collection->put('price', 100);
// ['product_id' => 1, 'name' => 'Desk', 'price' => 100]

$collection = collect(['product_id' => 'prod-100', 'name' => 'Desk']);

$collection->pull('name');
// 'Desk'(collection は ['product_id' => 'prod-100'] になる)

$collection = collect(['name' => 'Taylor', 'framework' => 'Laravel']);

$collection->forget('name');
// ['framework' => 'Laravel']

$collection->forget(['name', 'framework']);
// []

splice は、指定の位置から切り取って返します。2番目の引数で個数、3番目の引数でかわりに入れる要素を決められます。

php
$collection = collect([1, 2, 3, 4, 5]);

$chunk = $collection->splice(2);

$chunk->all();

// [3, 4, 5]

$collection->all();

// [1, 2]
php
$collection = collect([1, 2, 3, 4, 5]);

$chunk = $collection->splice(2, 1);

$chunk->all();

// [3]

$collection->all();

// [1, 2, 4, 5]
php
$collection = collect([1, 2, 3, 4, 5]);

$chunk = $collection->splice(2, 1, [10, 11]);

$chunk->all();

// [3]

$collection->all();

// [1, 2, 10, 11, 4, 5]

transform は、map の「元を書きかえる版」です。

php
$collection = collect([1, 2, 3, 4, 5]);

$collection->transform(function (int $item, int $key) {
    return $item * 2;
});

$collection->all();

// [2, 4, 6, 8, 10]

そのほかの例#

php
// avg・average
collect([['foo' => 10], ['foo' => 10], ['foo' => 20], ['foo' => 40]])->avg('foo');
// 20

// count と countBy
collect([1, 2, 2, 2, 3])->countBy()->all();
// [1 => 1, 2 => 3, 3 => 1]

$collection = collect(['alice@gmail.com', 'bob@yahoo.com', 'carlos@gmail.com']);

$collection->countBy(function (string $email) {
    return substr(strrchr($email, '@'), 1);
})->all();
// ['gmail.com' => 2, 'yahoo.com' => 1]

// flip
collect(['name' => 'Taylor', 'framework' => 'Laravel'])->flip()->all();
// ['Taylor' => 'name', 'Laravel' => 'framework']

// hasMany と hasSole
collect([1, 2, 3])->hasMany();
// true

collect(['1'])->hasSole();
// true

collect([1, 2, 3])->hasSole(fn (int $item) => $item === 2);
// true

// implode
collect([
    ['account_id' => 1, 'product' => 'Desk'],
    ['account_id' => 2, 'product' => 'Chair'],
])->implode('product', ', ');
// 'Desk, Chair'

collect([1, 2, 3, 4, 5])->implode('-');
// '1-2-3-4-5'

// join
collect(['a', 'b', 'c'])->join(', ');              // 'a, b, c'
collect(['a', 'b', 'c'])->join(', ', ', and ');    // 'a, b, and c'
collect(['a', 'b'])->join(', ', ' and ');          // 'a and b'
collect(['a'])->join(', ', ' and ');               // 'a'
collect([])->join(', ', ' and ');                  // ''

// keys
collect([
    'prod-100' => ['product_id' => 'prod-100', 'name' => 'Desk'],
    'prod-200' => ['product_id' => 'prod-200', 'name' => 'Chair'],
])->keys()->all();
// ['prod-100', 'prod-200']

// values
collect([
    10 => ['product' => 'Desk', 'price' => 200],
    11 => ['product' => 'Speaker', 'price' => 400],
])->values()->all();
// [0 => ['product' => 'Desk', 'price' => 200], 1 => ['product' => 'Speaker', 'price' => 400]]

// value
collect([
    ['product' => 'Desk', 'price' => 200],
    ['product' => 'Speaker', 'price' => 400],
])->value('price');
// 200

// multiply
collect([['name' => 'User #1'], ['name' => 'User #2']])->multiply(3);
// User #1・User #2 が、3回くり返される(全部で6件)

// nth
collect(['a', 'b', 'c', 'd', 'e', 'f'])->nth(4);
// ['a', 'e']

collect(['a', 'b', 'c', 'd', 'e', 'f'])->nth(4, 1);
// ['b', 'f'](2番目の引数は、はじめの位置)

// only と except
collect(['product_id' => 1, 'name' => 'Desk', 'price' => 100, 'discount' => false])
    ->only(['product_id', 'name'])->all();
// ['product_id' => 1, 'name' => 'Desk']

collect(['product_id' => 1, 'price' => 100, 'discount' => false])
    ->except(['price', 'discount'])->all();
// ['product_id' => 1]

// pad(マイナスなら左を埋める)
collect(['A', 'B', 'C'])->pad(5, 0)->all();
// ['A', 'B', 'C', 0, 0]

collect(['A', 'B', 'C'])->pad(-5, 0)->all();
// [0, 0, 'A', 'B', 'C']

// random
collect([1, 2, 3, 4, 5])->random();
// 4(ランダム)

collect([1, 2, 3, 4, 5])->random(3)->all();
// [2, 4, 5](ランダム)

// range・times
collect()->range(3, 6)->all();
// [3, 4, 5, 6]

Collection::times(10, function (int $number) {
    return $number * 9;
})->all();
// [9, 18, 27, 36, 45, 54, 63, 72, 81, 90]

// reverse
collect(['a', 'b', 'c', 'd', 'e'])->reverse()->all();
// [4 => 'e', 3 => 'd', 2 => 'c', 1 => 'b', 0 => 'a']

// select
collect([
    ['name' => 'Taylor Otwell', 'role' => 'Developer', 'status' => 'active'],
    ['name' => 'Victoria Faith', 'role' => 'Researcher', 'status' => 'active'],
])->select(['name', 'role']);
// [['name' => 'Taylor Otwell', 'role' => 'Developer'], ['name' => 'Victoria Faith', 'role' => 'Researcher']]

// shuffle
collect([1, 2, 3, 4, 5])->shuffle()->all();
// [3, 2, 5, 1, 4](ランダム)

// toArray・toJson・toPrettyJson
collect(['name' => 'Desk', 'price' => 200])->toArray();
// ['name' => 'Desk', 'price' => 200]

collect(['name' => 'Desk', 'price' => 200])->toJson();
// '{"name":"Desk","price":200}'

// unwrap と wrap
Collection::unwrap(collect('John Doe'));
// ['John Doe']

Collection::unwrap('John Doe');
// 'John Doe'

Collection::wrap('John Doe')->all();
// ['John Doe']

// zip
collect(['Chair', 'Desk'])->zip([100, 200])->all();
// [['Chair', 100], ['Desk', 200]]

注意

toArray は、中にある Arrayable(配列に変えられるという印を持つクラス。Eloquent のモデルなど)のオブジェクトも配列に変えます。変えずに、中の配列をそのまま取り出したいときは all を使います。

random は、要素の数より多い数を指定すると InvalidArgumentException を投げます。数を決める関数も渡せます。

php
use Illuminate\Support\Collection;

$random = $collection->random(fn (Collection $items) => min(10, count($items)));

$random->all();

// [1, 2, 3, 4, 5] - (retrieved randomly)

collect は、遅延コレクションをふつうのコレクションに変えるときに主に使います。

php
$lazyCollection = LazyCollection::make(function () {
    yield 1;
    yield 2;
    yield 3;
});

$collection = $lazyCollection->collect();

$collection::class;

// 'Illuminate\Support\Collection'

$collection->all();

// [1, 2, 3]

補足

Enumerable(Collection と LazyCollection が両方持つ約束ごと)のオブジェクトから、遅延しないふつうのコレクションを得たいときも、collect が使えます。

lazy は、とても大きなコレクションを、メモリ(コンピューターが作業中のデータを置いておく場所)をあまり使わずに絞りこむときに便利です。

php
$count = $hugeCollection
    ->lazy()
    ->where('country', 'FR')
    ->where('balance', '>', '100')
    ->count();

元のコレクションは、中身をメモリに持ったままです。しかし、そのあとの絞りこみでは、新しくメモリをほとんど使いません。

dump は中身を表示して処理をつづけ、dd は表示して止めます。

php
$collection = collect(['John Doe', 'Jane Doe']);

$collection->dump();

$collection->dd();

/*
    array:2 [
        0 => "John Doe"
        1 => "Jane Doe"
    ]
*/

補足

Eloquent のコレクション(モデルが入ったコレクション)では、contains・containsStrict・diff・except・intersect・only・partition・unique の動きが少し変わります。くわしくはEloquent のコレクションのページを見てください。

Higher Order Messages(高階メッセージ)#

コレクションには「ハイヤー・オーダー・メッセージ」という近道があります。関数を書かずに、よくある作業を短く書けます。次のメソッドで使えます:average・avg・contains・each・every・filter・first・flatMap・groupBy・keyBy・map・max・min・partition・reject・skipUntil・skipWhile・some・sortBy・sortByDesc・sum・takeUntil・takeWhile・unique。

使うときは、メソッド名を「プロパティ(クラスの中の変数)」のように書き、そのあとに、各要素に呼びたいメソッドやプロパティを続けます。たとえば each で、全員のメソッドを呼べます。

php
use App\Models\User;

$users = User::where('votes', '>', 500)->get();

$users->each->markAsVip();

sum なら、全員の votes の合計が出せます。

php
$users = User::where('group', 'Development')->get();

return $users->sum->votes;

遅延コレクション(Lazy Collections)#

注意

遅延コレクションを学ぶ前に、PHP のジェネレータ(値を1つずつ返す関数のしくみ)を知っておくと、分かりやすくなります。

遅延コレクションとは#

LazyCollection は、PHP のジェネレータを使って、とても大きなデータを少ないメモリで扱うための入れ物です。ふつうのコレクションが「全部を一度に手元へ置く」のに対し、遅延コレクションは「必要になった1つずつを取り出す」ので、メモリを節約できます。

たとえば、数ギガバイトのログファイルを読むとき、ファイルを丸ごと読みこむ代わりに、ごく一部だけをメモリに置いて処理できます。

php
use App\Models\LogEntry;
use Illuminate\Support\LazyCollection;

LazyCollection::make(function () {
    $handle = fopen('log.txt', 'r');

    while (($line = fgets($handle)) !== false) {
        yield $line;
    }

    fclose($handle);
})->chunk(4)->map(function (LazyCollection $lines) {
    return LogEntry::fromLines($lines);
})->each(function (LogEntry $logEntry) {
    // Process the log entry...
});

また、Eloquent のモデルが1万件あるとき、ふつうのコレクションだと、1万件が一度にメモリへ載ります。

php
use App\Models\User;

$users = User::all()->filter(function (User $user) {
    return $user->id > 500;
});

クエリビルダ(SQL を書かずに、メソッドをつないでデータベースに問い合わせるしくみ)の cursor メソッドは LazyCollection を返します。データベースへの問い合わせは1回のまま、メモリにはモデルが1件ずつしか載りません。次の例では、filter の関数は、ユーザーを1人ずつ取り出すときにはじめて実行されます。

php
use App\Models\User;

$users = User::cursor()->filter(function (User $user) {
    return $user->id > 500;
});

foreach ($users as $user) {
    echo $user->id;
}

遅延コレクションを作る#

make メソッドに、PHP のジェネレータ関数を渡します。

php
use Illuminate\Support\LazyCollection;

LazyCollection::make(function () {
    $handle = fopen('log.txt', 'r');

    while (($line = fgets($handle)) !== false) {
        yield $line;
    }

    fclose($handle);
});

Enumerable(共通の約束)#

Collection のほぼすべてのメソッドは、LazyCollection でも使えます。両方とも、Illuminate\Support\Enumerable という約束ごと(コントラクト)を持っているためです。Enumerable が決めているメソッドは、次のとおりです。

名前 名前 名前
all average avg
chunk chunkBy chunkWhile
collapse collect combine
concat contains containsStrict
count countBy crossJoin
dd diff diffAssoc
diffKeys dump duplicates
duplicatesStrict each eachSpread
every except filter
first firstOrFail firstWhere
flatMap flatten flip
forPage get groupBy
has implode intersect
intersectAssoc intersectByKeys isEmpty
isNotEmpty join keyBy
keys last macro
make map mapInto
mapSpread mapToGroups mapWithKeys
max median merge
mergeRecursive min mode
nth only pad
partition pipe pluck
random reduce reduceInto
reject replace replaceRecursive
reverse search shuffle
skip slice sole
some sort sortBy
sortByDesc sortKeys sortKeysDesc
split sum take
tap times toArray
toJson union unique
uniqueStrict unless unlessEmpty
unlessNotEmpty unwrap values
when whenEmpty whenNotEmpty
where whereStrict whereBetween
whereIn whereInStrict whereInstanceOf
whereNotBetween whereNotIn whereNotInStrict
wrap zip

注意

shift・pop・prepend のように、コレクション自体を書きかえるメソッドは、LazyCollection では使えません。

遅延コレクションだけのメソッド#

Enumerable のメソッドのほかに、LazyCollection には次のメソッドがあります。

名前 説明
takeUntilTimeout 指定の時刻まで要素を取り出し、それ以降は止まる
tapEach 要素が1つずつ取り出されるたびに、関数を呼ぶ
throttle 指定の秒数ごとに1つずつ値を返す。ほかのサービスの API に回数の制限があるときに便利
remember 取り出し済みの値を覚えて、次からは取り直さない
withHeartbeat 取り出しているあいだ、決まった間隔で関数を実行する

takeUntilTimeout は、指定の時刻まで取り出して止まります。

php
$lazyCollection = LazyCollection::times(INF)
    ->takeUntilTimeout(now()->plus(minutes: 1));

$lazyCollection->each(function (int $number) {
    dump($number);

    sleep(1);
});

// 1
// 2
// ...
// 58
// 59

たとえば、データベースの請求書を cursor で順に送るアプリがあるとします。15分おきに動くスケジュール(決まった時刻に動かすしくみ)にして、最大14分だけ処理させる、という使い方ができます。

php
use App\Models\Invoice;
use Illuminate\Support\Carbon;

Invoice::pending()->cursor()
    ->takeUntilTimeout(
        Carbon::createFromTimestamp(LARAVEL_START)->add(14, 'minutes')
    )
    ->each(fn (Invoice $invoice) => $invoice->submit());

each は、呼んですぐに全要素へ関数を実行します。一方 tapEach は、要素が1つずつ取り出されるときにはじめて関数を呼びます。

php
// ここまでは、まだ何も表示されない
$lazyCollection = LazyCollection::times(INF)->tapEach(function (int $value) {
    dump($value);
});

// 3つ取り出すので、3つ表示される
$array = $lazyCollection->take(3)->all();

// 1
// 2
// 3

throttle は、1つ取り出すごとに、指定の秒数を待ちます。

php
use App\Models\User;

User::where('vip', true)
    ->cursor()
    ->throttle(seconds: 1)
    ->each(function (User $user) {
        // Call external API...
    });

remember は、取り出した値を覚えておきます。

php
// まだ問い合わせは実行されていない
$users = User::cursor()->remember();

// 問い合わせが実行される
// 最初の5人をデータベースから取り出す
$users->take(5)->all();

// 最初の5人は、覚えておいたものを使う
// 残りは、データベースから取り出す
$users->take(20)->all();

withHeartbeat は、時間のかかる処理のあいだ、決まった間隔で関数を実行します。ロック(ほかの処理が同じ仕事を同時にしないための鍵)の延長や、進み具合の知らせに便利です。

php
use Carbon\CarbonInterval;
use Illuminate\Support\Facades\Cache;

$lock = Cache::lock('generate-reports', seconds: 60 * 5);

if ($lock->get()) {
    try {
        Report::where('status', 'pending')
            ->lazy()
            ->withHeartbeat(
                CarbonInterval::minutes(4),
                fn () => $lock->refresh()
            )
            ->each(fn ($report) => $report->process());
    } finally {
        $lock->release();
    }
}

関連するページ#

公式ドキュメント(英語)

2026年10月5日時点の内容をもとに、日本語でまとめています。

ページの一覧