web-dev-qa-db-ja.com

雄弁なコレクション:各vs foreach

Eloquentコレクションに固有の質問ではないかもしれませんが、コレクションを操作していると、私に衝撃を与えます。 _$collection_のインスタンスである_Illuminate\Support\Collection_オブジェクトがあると仮定しましょう。

ここで繰り返したい場合、クロージャと通常のforeachを使用してeach()を使用することの長所と短所は何ですか。何かありますか?

_foreach ($collection as $item) {
    // Some code
}

--- VS ---

$collection->each(function ($item) {
    // Some code
});
_
44
Niklas Modess

foreachステートメントは、コレクションを循環し、コレクションに対して何らかのロジックを実行する一種の方法として使用する必要があります。その中にあるものがプログラムの他の事柄に影響する場合、このループを使用します。

_.each_メソッドは、_array_map_を使用して、コレクション内の各オブジェクトを循環させ、各オブジェクトに対してクロージャを実行します。次に、結果の配列を返します。それが鍵です!何らかの方法でコレクションを変更する場合は、_.each_を使用する必要があります。多分それは車の配列であり、モデルを大文字または小文字にする必要があります。オブジェクトを取り、各Carオブジェクトのモデルでstrtoupper()を呼び出す_.each_メソッドにクロージャーを渡すだけです。次に、行われた変更を含むコレクションを返します。

物語のモラールは次のとおりです。_.each_メソッドを使用して、配列の各項目を何らかの方法で変更します。 foreachループを使用して各オブジェクトを使用し、プログラムの他の部分に影響を与えます(ロジックステートメントを使用)。

更新(2015年6月7日)

以下のように(私がそこで何をしたかを参照してください)雄弁に述べたように、上記の答えは少しずれています。 _.each_を使用する_array_map_メソッドは、実際に_array_map_呼び出しからの出力を使用しませんでした。したがって、_array_map_によって作成された新しい配列は、Collectionに保存されません。変更するには、Collectionオブジェクトにも存在する_.map_メソッドを使用することをお勧めします。

foreachステートメントを使用することは、useステートメントを使用していることを確認しない限り、クロージャーの外部の変数にアクセスできないため、少しずつ理にかなっています。気まずい。

上記の回答が最初に記述されたときの実装は、 ここにあります になります。

_.each_ in Laravel 5.1

以下で説明している新しい_.each_は、_array_map_を使用しなくなりました。コレクション内の各アイテムを繰り返し処理し、渡された_$callback_を呼び出して、アイテムと配列内のキーを渡します。機能的には、同じように動作するようです。コードを読むとき、foreachループを使用する方が理にかなっていると思います。ただし、_.each_を使用する利点は、それがあなたの空想をくすぐる場合にメソッドを一緒に連鎖させることができるためです。また、ビジネスロジックでできるようにする必要がある場合、コールバックからfalseを返してループを早期に終了することもできます。

新しい実装の詳細については、 ソースコード をご覧ください。

51
searsaw

既存の回答には、多くの紛らわしい誤報があります。

短い答え

簡単な答えは次のとおりです。.each()foreachを使用してLaravelコレクションを繰り返し処理する)には大きな違いはありません。どちらの手法でも同じ結果が得られます。

アイテムの変更はどうですか?

アイテムを変更するかどうかは、.each()foreachを使用するかどうかとは無関係です。どちらも、私たちが話しているアイテムの種類に応じて、コレクション内のアイテムを変更することができます(できません!)。

  • コレクションにオブジェクトが含まれる場合のアイテムの変更:コレクションがPHPオブジェクト(Eloquentコレクションなど))のセットである場合、.each()またはforeachを使用すると、オブジェクトのプロパティを変更できます(_$item->name = 'foo'_など)。これは、単にPHPオブジェクトが常に参照のように機能するためです。オブジェクト全体を別のオブジェクトに置き換えるには(あまり一般的ではないシナリオ)、代わりに.map()を使用します。
  • コレクションに非オブジェクトが含まれる場合のアイテムの変更:これはあまり一般的ではありませんが、コレクションに文字列などの非オブジェクトが含まれる場合、.each()は方法を提供しませんコレクションアイテムの値を変更します。 (クロージャの戻り値は無視されます。)代わりに.map()を使用してください。

だから...どれを使うべきですか?

パフォーマンスについて疑問がある場合は、Eloquentアイテムと文字列のコレクションの両方の大規模なコレクションでいくつかのテストを行いました。どちらの場合も、foreachの使用は.each()よりも高速でした。しかし、私たちはマイクロ秒について話している。ほとんどの実際のシナリオでは、データベースなどにアクセスするのにかかる時間と比較して、速度の差はそれほど大きくありません。

それは主に個人的な好みに帰着します。 .each()を使用すると、複数の操作(たとえば、.where(...).each(...))をチェーン化できるため便利です。私はそれぞれの状況で最もきれいに見えるものに応じて、自分のコードで両方を使用する傾向があります。

13
orrd

技術的に言えば、他の2つの答えが言っているのとは反対に、Collection::each()notでコレクション内のアイテムの値を変更します。 array_map()を使用しますが、その呼び出しの結果は保存しません。

コレクション内の各アイテムを変更したい場合(たとえば、現在受け入れられている回答へのコメントでDamonとしてオブジェクトにキャストする場合)、Collection::map()を使用する必要があります。これにより、array_map()の基礎となる呼び出しの結果に基づいて新しいコレクションが作成されます。

3
Jake S

後者のeach()を使用する方がより有益です。

条件を連鎖させて、より明確で表現力豊かなコードを書くことができます。例:

$ example-> each()-> map()-> filter();

これにより、どのように達成するかではなく、何を達成するかをコンピューターに指示する宣言型プログラミングに近づきます。

役に立つ記事:

https://martinfowler.com/articles/collection-pipeline/

https://adamwathan.me/refactoring-to-collections/

0
Nik K