Edited at

【5.5対応】Laravel の Collection を使い倒してみたくなった 〜 サンプルコード 115 連発 2/3

More than 1 year has passed since last update.


はじめに

以下の記事の続編です。

【5.5対応版】 Laravel の Collection を使い倒してみたくなった 〜 サンプルコード 115 連発 1/3

では、第二弾として 39. groupBy - 76. reverse までをお送りします。


39. groupBy

Collection groupBy(callable|string $groupBy, bool $preserveKeys = false)

キーまたはコールバック関数を用いて要素をグルーピングします。

$items = new Collection([

['id' => 1, 'name' => 'John Doe', 'score' => 10],
['id' => 2, 'name' => 'Jane Doe', 'score' => 8],
['id' => 3, 'name' => 'Adam Smith', 'score' => 10],
['id' => 4, 'name' => 'John Rock', 'score' => 8],
]);
// key
$groupedByScore = $items->groupBy('score');
$groupedByScore->each(function ($group, $score) {
$group->each(function ($entity) use ($score) {
$this->assertEquals($score, $entity['score']);
});
});

// closure
// スコアが10以上のグループと、10未満のグループに分ける
$groupedByScore = $items->groupBy(function ($item) {
return $item['score'] >= 10 ? 0 : 1;
});

$lteTen = $groupedByScore->first()->every(function ($item) {
return $item['score'] >= 10;
});
$this->assertEquals(true, $lteTen);
$lessTen = $groupedByScore->last()->every(function ($item) {
return $item['score'] < 10;
});
$this->assertEquals(true, $lessTen);


40. keyBy

Collection keyBy(callable|string $keyBy)

キーまたはコールバック関数を用いて、連想配列化します。

$items = new Collection([

['id' => 1, 'name' => 'John Doe', 'score' => 10],
['id' => 2, 'name' => 'Jane Doe', 'score' => 8],
]);
// key
$keyed = $items->keyBy('id');
$this->assertEquals([1, 2], $keyed->keys()->toArray());
$this->assertEquals($items->values(), $keyed->values());

// closure
$keyed = $items->keyBy(function ($item) {
return Collection::make($item)->only(['id', 'name'])->implode('-');
});
$this->assertEquals(['1-John Doe', '2-Jane Doe'], $keyed->keys()->toArray());
$this->assertEquals($items->values(), $keyed->values());


41. has

bool has(mixed $key)

コレクションに指定されたキーを持つ要素があるかどうかを判定します。

$entity = new Collection(['id' => 1, 'name' => 'John Doe', 'score' => 10]);

$this->assertEquals(true, $entity->has('name'));

5.5 からは配列を指定できるようになりました。

$entity = new Collection(['id' => 1, 'name' => 'John Doe', 'score' => 10]);

$this->assertEquals(true, $entity->has(['id', 'name']));


42. implode

string implode(string $value, string $glue = null)

指定された文字列で値を連結します。連想配列のコレクションの場合は、キーを指定すれば、そのキーを持つ値を連結します。

// associative array

$entity = new Collection(['id' => 1, 'name' => 'John Doe', 'score' => 10]);
$imploded = $entity->implode(',');
$this->assertEquals('1,John Doe,10', $imploded);

// array of array
$items = new Collection([
['id' => 1, 'name' => 'John Doe', 'score' => 10],
['id' => 2, 'name' => 'Jane Doe', 'score' => 8],
]);
$imploded = $items->implode('name', ',');
$this->assertEquals('John Doe,Jane Doe', $imploded);


43. intersect

Collection intersect(mixed $items)

別の配列(コレクション)との共通項を返します(array_intersect のラッパーです)。

$first = new Collection([1, 2, 3]);

$second = new Collection([3, 4, 5]);
$intersected = $first->intersect($second);
$this->assertEquals([2 => 3], $intersected->toArray());


44. intersectByKeys (5.5)

Collection intersectByKeys(mixed $items)

intersect同様、2つの配列の共通項を返しますが、キーを基準にします(array_intersect_key のラッパーです)。

$first = new Collection(['id' => 1, 'name' => 'John Doe', 'birthday' => '2000-05-01']);

$second = new Collection(['id' => 2, 'name' => 'Jane Doe']); // or just an array
$itemsHasCommonKey = $first->intersectByKeys($second);
$this->assertEquals(['id' => 1, 'name' => 'John Doe'], $itemsHasCommonKey->toArray());


45. isEmpty

bool isEmpty()

コレクションが空かどうかを判定します。

$empty = new Collection();

$this->assertEquals(true, $empty->isEmpty());

$filled = new Collection([1, 2, 3]);
$this->assertEquals(false, $filled->isEmpty());


46. isNotEmpty

bool isNotEmpty()

コレクションが空でないかどうかを判定します。

$empty = new Collection();

$this->assertEquals(false, $empty->isNotEmpty());

$filled = new Collection([1, 2, 3]);
$this->assertEquals(true, $filled->isNotEmpty());


47. keys

Collection keys()

各要素のキーを返します。

$entity = new Collection(['id' => 1, 'name' => 'John Doe', 'score' => 10]);

$keys = $entity->keys();
$this->assertEquals(['id', 'name', 'score'], $keys->toArray());


48. last

mixed last(callable $callback = null, mixed $default = null)

最後の要素を返します。

$items = new Collection([1, 2, 3]);

$last = $items->last();
$this->assertEquals(3, $last);


49. pluck

Collection pluck(string|array $value, string|null $key = null)

指定されたキーの要素を返します。配列で返すパターンと連想配列で返すパターンがあります。


49.1. 配列で返すパターン(値となるキーのみ指定)

$items = new Collection([

['id' => 1, 'name' => 'John Doe', 'score' => 10],
['id' => 2, 'name' => 'Jane Doe', 'score' => 8],
]);
// only key
$plucked = $items->pluck('id');
$this->assertEquals([1, 2], $plucked->toArray());


49.2. 連想配列で返すパターン(値となるキーと、キーとなるキーを指定)

「キーとなるキー」というのが分かりにくいですが、上の例が配列を返すのに対し、こちらのバージョンでは連想配列になります。

両者の結果を比べれば、どう違うのかが分かると思います。

$items = new Collection([

['id' => 1, 'name' => 'John Doe', 'score' => 10],
['id' => 2, 'name' => 'Jane Doe', 'score' => 8],
]);
// value and key
$plucked = $items->pluck('name', 'id');
$this->assertEquals([1 => 'John Doe', 2 => 'Jane Doe'], $plucked->toArray());


50. map

Collection map(callable $callback)

各要素にコールバック関数を適用します。

$items = new Collection([

['id' => 1, 'name' => 'John Doe', 'score' => 10],
['id' => 2, 'name' => 'Jane Doe', 'score' => 8],
]);
$doubledScores = $items->map(function ($item) {
return $item['score'] * 2;
});
$this->assertEquals([20, 16], $doubledScores->toArray());


51. mapSpread (5.5)

Collection mapSpread(callable $callback)

eachSpread 同様、配列の要素を展開してコールバック関数に渡します。

$items = new Collection([

[10, 15, 10],
[ 8, 14, 12],
]);
$squares = $items->mapSpread(function ($width, $height, $depth, $key) {
return $width * $height * $depth;
});
$this->assertEquals([1500, 1344], $squares->toArray());


52. mapToDictionary (5.5)

Collection mapToDictionary(callable $callback)

mapToGroups に似ていますが、グルーピングされた要素は、Collection ではなく、生の配列になります。

$items = new Collection([

['id' => 1, 'name' => 'John Doe', 'score' => 10],
['id' => 2, 'name' => 'Jane Doe', 'score' => 8],
['id' => 3, 'name' => 'Adam Smith', 'score' => 10],
['id' => 4, 'name' => 'John Rock', 'score' => 8],
]);
$groupedByScore = $items->mapToDictionary(function ($item) {
return [$item['score'] => $item['name']];
});
$this->assertEquals([
8 => ['Jane Doe', 'John Rock'],
10 => ['John Doe', 'Adam Smith']
], $groupedByScore->toArray());


53. mapToGroups

Collection mapToGroups(callable $callback)

各要素にキーバリューペアを返すコールバック関数を適用し、キーでグルーピングします。

$items = new Collection([

['id' => 1, 'name' => 'John Doe', 'score' => 10],
['id' => 2, 'name' => 'Jane Doe', 'score' => 8],
['id' => 3, 'name' => 'Adam Smith', 'score' => 10],
['id' => 4, 'name' => 'John Rock', 'score' => 8],
]);
$groupedByScore = $items->mapToGroups(function ($item) {
return [$item['score'] => $item['name']];
});
$this->assertEquals([
8 => ['Jane Doe', 'John Rock'],
10 => ['John Doe', 'Adam Smith']
], $groupedByScore->toArray());


54. mapWithKeys

Collection mapWithKeys(callable $callback)

キーバリューペアを返すコールバック関数を各要素に適用し、そのキーバリューを元に連想配列を構築して返します。

$items = new Collection([

['id' => 1, 'name' => 'John Doe', 'score' => 10],
['id' => 2, 'name' => 'Jane Doe', 'score' => 8],
]);
$idNames = $items->mapWithKeys(function ($v, $k) {
return [$v['id'] => $v['name'] . ' (' . $v['score'] . ')'];
});
$this->assertEquals([1 => 'John Doe (10)', 2 => 'Jane Doe (8)'], $idNames->toArray());


55. flatMap

Collection flatMap(callable $callback)

各要素にコールバック関数を適用したのち、フラット化します(が、内部で呼んでいるのは、flatten ではなくて collapse です。なんで??)

$items = new Collection([1, 2, 3]);

$flatMapped = $items->flatMap(function ($item) {
return Collection::times(3, function ($n) use ($item) {
return $item * $n;
});
});
$this->assertEquals([1, 2, 3, 2, 4, 6, 3, 6, 9], $flatMapped->toArray());


56. mapInto (5.5)

Collection mapInto(string $class)

これはちょっと面白いメソッドですね。コレクションの各要素で、与えられたクラスを初期化して返します。

テスト用に以下のクラスを用意しました。

php

class UserWithScore

{

// (snip)

public function __construct(array $values, int $userId)

{

$this->userId = $userId;

$this->values = $values;

}

// (snip)

}

mapInto を使って UserWithScore インスタンスのコレクションを返すようにします。

ポイントは、コレクションの要素をキーバリューのペアにして、マップするクラスのコンストラクタはそれらを初期化のパラメータにする、ということです(キーは使わなくてもいいです)。

$items = new Collection([

1 => ['id' => 1, 'name' => 'John Doe', 'score' => 10],
2 => ['id' => 2, 'name' => 'Jane Doe', 'score' => 8],
]);
$userWithScores = $items->mapInto(UserWithScore::class);
$scores = [1 => 10, 2 => 8];
foreach ($userWithScores as $id => $userWithScore) {
$this->assertInstanceOf(UserWithScore::class, $userWithScore);
$this->assertEquals($id, $userWithScore->userId());
$this->assertEquals($scores[$id], $userWithScore->score());
}


57. max

mixed max(callable|string|null $callback = null)

指定されたキーの最大値を返します。


57.1. 引数なしのパターン

$items = new Collection([1, 2, 3, 2, 1]);

$max = $items->max();
$this->assertEquals(3, $max);


57.2. キーを指定するパターン

$items = new Collection([

['id' => 1, 'name' => 'John Doe', 'score' => 10],
['id' => 2, 'name' => 'Jane Doe', 'score' => 8],
]);
$max = $items->max('score');
$this->assertEquals(10, $max);


57.3. クロージャを指定するバターン

$items = new Collection([

['id' => 1, 'name' => 'John Doe', 'score' => 10],
['id' => 2, 'name' => 'Jane Doe', 'score' => 8],
]);
$max = $items->max(function ($item) {
return $item['score'];
});
$this->assertEquals(10, $max);


58. merge

Collection merge(mixed $items)

別のコレクション(配列)をマージします。

$first = new Collection([1, 2, 3]);

$second = new Collection([4, 5, 6]);
$merged = $first->merge($second);
$this->assertEquals([1, 2, 3, 4, 5, 6], $merged->toArray());


59. combine

Collection combine(mixed $values)

自身の要素をキーとして、別のコレクション(配列)を値として、別のコレクションを生成します(内部で array_combine を呼んでいます)。

$keys = new Collection(['id', 'name', 'score']);

$values = new Collection([1, 'John Doe', 8]);
$combined = $keys->combine($values);
$this->assertEquals(['id' => 1, 'name' => 'John Doe', 'score' => 8], $combined->toArray());


60. union

Collection union(mixed $items)

別のコレクション(配列)との和集合を返します(内部では配列を加算しています $first + $second)。

$first = new Collection(['a' => 1, 'b' => 2, 'c' => 3]);

$second = new Collection(['a' => 4, 'd' => 5]);
$unioned = $first->union($second);
$this->assertEquals(['a' => 1, 'b' => 2, 'c' => 3, 'd' => 5], $unioned->toArray());


61. min

mixed min(callable|string|null $callback = null)

指定されたキーの最小値を返します。キーが指定されていなければ値を元に算出します。


61.1. 引数なしのパターン

$items = new Collection([1, 2, 3, 2, 1]);

$min = $items->min();
$this->assertEquals(1, $min);


61.2. キーを指定するパターン

$items = new Collection([

['id' => 1, 'name' => 'John Doe', 'score' => 10],
['id' => 2, 'name' => 'Jane Doe', 'score' => 8],
]);
$min = $items->min('score');
$this->assertEquals(8, $min);


61.3. クロージャを渡すパターン

$min = $items->min(function ($item) {

return $item['score'];
});
$this->assertEquals(8, $min);


62. nth

Collection nth(int $step, int $offset)

てっきり n 番目の要素を取ってくるかと思いきや、 n 個置きに要素を集めて新たなコレクションを作成して返します($offset が指定されていれば、そこを起点にします)。

$items = new Collection([1, 2, 3, 4, 5]);

// without offset
$picked = $items->nth(2);
$this->assertEquals([1, 3, 5], $picked->toArray());

// with offset
$picked = $items->nth(2, 1);
$this->assertEquals([2, 4], $picked->toArray());


63. only

Collection only(mixed $keys)

except の逆で、指定されたキーを持つ要素のみを返します。

$entity = new Collection(['id' => 1, 'name' => 'John Doe', 'score' => 10]);

// keys exist
$picked = $entity->only(['id', 'score']);
$this->assertEquals(['id' => 1, 'score' => 10], $picked->toArray());

// not exist
$picked = $entity->only('missing attribute');
$this->assertEquals([], $picked->toArray());


64. forPage

Collection forPage(int $page, int $perPage)

パジネーションで使用します。

下記の例では、1 ページの件数を 5 としたときの 2 ページ目のコレクションを返します。

$items = Collection::times(20, function ($n) {

return $n;
});
$partition = $items->forPage(2, 5);
$this->assertEquals([6, 7, 8, 9, 10], $partition->values()->toArray());


65. partition

Collection partition(callable|string $callback)

指定されたキーまたはコールバック関数でコレクションを分割します。


65.1. キーを指定するパターン

論理値を持つキーで分割します。

$items = new Collection([

['id' => 1, 'name' => 'John Doe', 'score' => 10, 'is_premium' => true],
['id' => 2, 'name' => 'Jane Doe', 'score' => 8, 'is_premium' => true],
['id' => 3, 'name' => 'Adam Smith', 'score' => 10, 'is_premium' => false],
['id' => 4, 'name' => 'John Rock', 'score' => 8, 'is_premium' => false],
]);
// key
$partitions = $items->partition('is_premium');
$this->assertEquals([
[
0 => ['id' => 1, 'name' => 'John Doe', 'score' => 10, 'is_premium' => true],
1 => ['id' => 2, 'name' => 'Jane Doe', 'score' => 8, 'is_premium' => true],
],
[
2 => ['id' => 3, 'name' => 'Adam Smith', 'score' => 10, 'is_premium' => false],
3 => ['id' => 4, 'name' => 'John Rock', 'score' => 8, 'is_premium' => false],
],
], $partitions->toArray());


65.2. クロージャを指定するパターン

論理値を返すクロージャで分割します。

// closure

$items = new Collection([
['id' => 1, 'name' => 'John Doe', 'score' => 10, 'is_premium' => true],
['id' => 2, 'name' => 'Jane Doe', 'score' => 8, 'is_premium' => true],
['id' => 3, 'name' => 'Adam Smith', 'score' => 10, 'is_premium' => false],
['id' => 4, 'name' => 'John Rock', 'score' => 8, 'is_premium' => false],
]);
$partitions = $items->partition(function ($item) {
return $item['score'] > 8;
});
$this->assertEquals([
[
0 => ['id' => 1, 'name' => 'John Doe', 'score' => 10, 'is_premium' => true],
2 => ['id' => 3, 'name' => 'Adam Smith', 'score' => 10, 'is_premium' => false],
],
[
1 => ['id' => 2, 'name' => 'Jane Doe', 'score' => 8, 'is_premium' => true],
3 => ['id' => 4, 'name' => 'John Rock', 'score' => 8, 'is_premium' => false],
],
], $partitions->toArray());


66. pipe

mixed pipe(callable $callback)

自身をコールバック関数に適用し、戻り値として新たなコレクションを返します。複数のコールバック関数を順に適用するために使います。

パイプでの受け渡しがコレクションのみに限定されるので、使う局面は限られます。

PHP でもパイプ演算子が使えるようになるといいなぁ。

$items = new Collection([

['id' => 1, 'name' => 'John Doe', 'score' => [10, 11, 12]],
['id' => 2, 'name' => 'Jane Doe', 'score' => [8, 9, 10]],
]);
$sumScore = function (Collection $items) {
return $items->pluck('score', 'id')->map(function ($scores) {
return Collection::make($scores)->sum();
});
};
$groupByScore = function (Collection $items) {
return $items->groupBy(function ($item, $key) {
return $key >= 30 ? '>=' : '<';
});
};
/** @var Collection $piped */
$piped = $items->pipe($sumScore)->flip()->pipe($groupByScore);
$this->assertEquals(['>=' => [1], '<' => [2]], $piped->toArray());


67. pop

mixed pop()

コレクションから最後の要素を取り除いて返します。スタックですね。

$items = new Collection([1, 2, 3]);

$pop = $items->pop();
$this->assertEquals(3, $pop);
$this->assertEquals([1, 2], $items->toArray());


68. prepend

$this prepend(mixed $value, mixed $key = null)

コレクションの先頭に要素を追加します。

$items = new Collection([1, 2, 3]);

$items->prepend(0);
$this->assertEquals([0, 1, 2, 3], $items->toArray());


69. push

$this push(mixed $value)

コレクションの末尾に要素を追加します。

$items = new Collection([1, 2, 3]);

$items->push(0);
$this->assertEquals([1, 2, 3, 0], $items->toArray());


70. concat (5.5)

$this concat(Traversable $source)

別のコレクション(Traversable の実装)を連結します。内部で連続して push を呼び出しているだけです。

$items = new Collection([1, 2, 3]);

$concatenated = $items->concat(new Collection([4, 5, 6]));
$this->assertEquals([1, 2, 3, 4, 5, 6], $concatenated->toArray());


71. pull

mixed pull(mixed $key, mixed $default = null)

コレクションから指定したキーを持つ要素を削除して返します。

$entity = new Collection(['id' => 1, 'name' => 'John Doe', 'score' => 8]);

$name = $entity->pull('name');
$this->assertEquals('John Doe', $name);
$this->assertEquals(['id' => 1, 'score' => 8], $entity->toArray());


72. put

$this put(mixed $key, mixed $value)

コレクションに指定したキーで要素を追加します。

$entity = new Collection(['id' => 1, 'name' => 'John Doe', 'score' => 8]);

$entity->put('birthday', '2000-01-01');
$this->assertEquals(['id' => 1, 'name' => 'John Doe', 'score' => 8, 'birthday' => '2000-01-01'], $entity->toArray());


73. random

mixed random(int|null $amount = 1)

コレクションからランダムに値を取り出します。$amount に 1 より大きい値を指定すると配列で返ります。

$items = new Collection([1, 2, 3]);

$random = $items->random();
$this->assertTrue(in_array($random, $items->toArray()));

$randoms = $items->random(2);
$this->assertEquals(1, $items->diff($randoms)->count());


74. reduce

mixed reduce(callable $callback, mixed $initial = null)

コレクションから単一の値を導出します。

下記の例では、各要素を二倍した値の合計値を計算しています。

$items = new Collection([1, 2, 3]);

$reduced = $items->reduce(function ($acc, $n) {
return $acc + $n * 2;
});
$this->assertEquals(12, $reduced);


75. reject

Collection reject(callable|mixed $callback)

条件に一致しない要素を除いた要素を返します。


75.1. 値を渡すパターン

$items = new Collection([1, 2, 3]);

$rejected = $items->reject(2);
$this->assertEquals([0 => 1, 2 => 3], $rejected->toArray());


75.2. クロージャを渡すバターン

$items = new Collection([

['id' => 1, 'name' => 'John Doe', 'score' => 10],
['id' => 2, 'name' => 'Jane Doe', 'score' => 8],
['id' => 3, 'name' => 'Adam Smith', 'score' => 10],
['id' => 4, 'name' => 'John Rock', 'score' => 8],
]);
$rejected = $items->reject(function ($item) {
return $item['score'] <= 8;
});
$this->assertEquals([1, 3], $rejected->pluck('id')->toArray());


76. reverse

Collection reverse()

配列を反転させます。

$items = new Collection([1, 2, 3]);

$reversed = $items->reverse();
$this->assertEquals([3, 2, 1], $reversed->values()->toArray());

以下次号