skip to content

Chained Collection Methods

Laravel's Collection wraps arrays in chainable methods like map, filter, groupBy and pluck, plus higher-order messages. Interviewers probe new-versus-mutated results and pipeline readability.

on this pageshow

explore

questions

6

In Laravel, what does collect() return, and does chaining map() and filter() on it change the original collection?

level: juniorimportance: must knowfreq 72%

answer

  1. a wrapper class, not an array
  2. Illuminate\Support\Collection
  3. each call hands back a new instance
  4. filter() with no callback drops falsy
  5. objects inside are shared, not copied

basics

~10 s

collect() wraps an array or iterable in an Illuminate\Support\Collection. map() and filter() each return a new Collection and leave the original untouched, which is what makes long chains safe to write.

solid answer

~40 s

`collect($items)` is a helper for `new Collection($items)`: it wraps an array (or any iterable, a single scalar, or `null` for an empty one) in `Illuminate\Support\Collection`. Most methods, including `map()`, `filter()` and `reject()`, build their result with `new static(...)`, so every step of a chain returns a fresh collection and the variable you started from keeps its items. That is why `$raw->filter(...)->map(...)` reads as a pipeline and why a bare `$raw->map(...)` with no assignment does nothing useful. `filter()` without a callback drops every falsy item (`0`, `''`, `null`, `false`, `[]`). The copy is shallow: if the items are objects, the old and new collections point at the same objects.

code

php · 13 lines
php
<?php

use Illuminate\Support\Collection;

$raw = collect([4, 0, 5, null, 3]);

$kept = $raw->filter();            // [0 => 4, 2 => 5, 4 => 3]
$doubled = $kept->map(fn (int $s) => $s * 2);

$raw->map(fn ($s) => $s * 100);   // result discarded: $raw is unchanged

var_dump($raw->count());          // int(5)
var_dump($doubled instanceof Collection); // bool(true)

go deeper

for a junior

Recall that collect() gives you an Illuminate\Support\Collection and that map(), filter() and reject() return new collections, so you must assign or chain the result.

for a middle

Explain the new static() contract, what filter() drops without a callback, and why the copy is shallow when the items are models or other objects.

for a senior

Point out where shared object handles let a supposedly pure chain mutate data used elsewhere, and set a team habit of returning new values from map callbacks.

for a principal

Weigh collection pipelines against plain loops for readability and allocation cost in hot paths, and decide where the codebase needs explicit copies rather than trusting immutability.

## What collect() gives you `collect()` is a global helper defined in the framework's collections package. Its body is one line: it returns `new Collection($value)`, where `Collection` is **`Illuminate\Support\Collection`**. The constructor normalises whatever you pass: - an **array** is stored as-is, keys included; - another **collection**, an `Arrayable` object or any **iterable** is converted to an array first; - a **scalar** (a string, an int) is wrapped into a one-item array; - **`null`** becomes an empty collection, so `collect()` and `collect(null)` are both safe starting points. Eloquent queries hand you a subclass, `Illuminate\Database\Eloquent\Collection`, when you call `get()`, so everything below applies to query results too. ## Returning a new instance: the default contract The Laravel docs put it plainly: in general, collections are **immutable** — each method returns an entirely new `Collection` instance. Internally, `map()`, `filter()`, `reject()` and most of their siblings call a protected `newInstance()` helper that runs `new static($items)`. Two consequences follow: 1. **Chaining is safe.** Every arrow in `$answers->filter(...)->map(...)->sortBy(...)` works on the previous step's result, never on `$answers` itself. 2. **Forgetting to assign is a silent no-op.** `$answers->map(fn ($a) => $a['score'] * 10);` computes a new collection and throws it away; `$answers` is unchanged. A handful of methods break the rule on purpose — `transform()`, `push()`, `put()`, `forget()`, `pop()` and a few others change the collection in place. Knowing that list is its own interview question; for the everyday screen, the point is that `map` and `filter` are *not* on it. ## map(), filter() and reject() in detail | Method | Callback receives | Returns | Typical use | |---|---|---|---| | `map($cb)` | `($value, $key)` | new collection of the callback's return values, **same keys** | reshape each item | | `filter($cb = null)` | `($value, $key)` | new collection of items where the callback returned truthy | keep what matches | | `reject($cb)` | `($value, $key)` | new collection of items where the callback returned falsy | drop what matches | Details worth saying out loud in an interview: - **`filter()` with no callback** falls back to PHP's `array_filter`, removing every falsy value: `0`, `'0'`, `''`, `null`, `false` and empty arrays. - **`reject()`** is `filter()` with the test inverted; passing a plain value instead of a closure rejects items loosely equal to it. - The callback's second argument is the **key**, which is handy for associative data such as survey answers keyed by respondent ID. ## Shallow, not deep "Returns a new collection" describes the **container**, not the things inside it. When a collection holds objects — Eloquent models, DTOs — the new collection holds the **same object handles**. A `filter()` result and its source therefore share models: change `$model->score` through one and the other sees it. Arrays and scalars are copied by value, so for plain arrays the question never comes up. ## Getting a plain array back - `all()` returns the underlying PHP array exactly as stored. - `toArray()` also converts nested `Arrayable` items (models, other collections) to arrays. - Returning a collection from a route or controller lets Laravel serialise it to JSON for you. ## A worked example ```php <?php $answers = collect([ ['department' => 'Sales', 'score' => 4], ['department' => 'IT', 'score' => 0], ['department' => 'Sales', 'score' => 5], ]); $scored = $answers ->filter(fn (array $a) => $a['score'] > 0) ->map(fn (array $a) => $a['score'] * 20); // $answers still holds all three rows // $scored holds [0 => 80, 2 => 100] ``` The original `$answers` still has three rows, and `$scored` is a separate collection. Notice the keys `0` and `2` in the result: `filter()` keeps the original keys, which matters as soon as the result is serialised to JSON. ## Why interviewers ask this The question checks two things at once: that the candidate knows the collection is a **class with a fluent API** rather than syntax sugar over arrays, and that they understand **which calls produce new values**. Candidates who believe `map()` edits in place write code that works by accident until someone reuses the source collection further down the method.

  • If map() returns a new collection, can a map callback still change the original items?
    Yes, when the items are objects. The new collection is a new container, but it holds the same object handles, so `$answer->score = 0` inside the callback changes the model that the original collection also holds. Return a new value or clone the object inside the callback when you need a real copy.
  • What is the difference between all() and toArray() on a Laravel collection?
    `all()` returns the internal PHP array exactly as stored, so Eloquent models stay model objects. `toArray()` walks the items and converts anything `Arrayable` — models, nested collections — into plain arrays, which is what you want before encoding or comparing structures.

saying these in an interview costs you the question

  • Says map() rewrites the collection it was called on.
  • Calls $collection->map(...) without assigning the result and expects a change.
  • Thinks collect() returns a plain PHP array with extra functions.
  • Believes filter() with no callback returns every item unchanged.
  • Assumes a new collection deep-copies the Eloquent models inside it.
open as a page

With Laravel collections, how do pluck(), keyBy() and groupBy() differ when summarising survey answers by department?

level: middleimportance: must knowfreq 58%

basics

~10 s

pluck() extracts one field per item, keyBy() indexes whole items by a field with one item per key, and groupBy() builds a collection of sub-collections, one per distinct value, so duplicates are kept.

open as a page

Why does a Laravel collection returned after filter() or sortBy() sometimes serialize to a JSON object instead of an array?

level: middleimportance: should knowfreq 45%

basics

~20 s

filter(), reject(), sortBy(), unique() and partition() keep each item's original key. Once the keys are no longer 0, 1, 2 in order, json_encode emits an object; calling values() last re-indexes the collection so it serializes as an array.

open as a page

What are higher-order messages on Laravel collections, such as $responses->each->markReviewed(), and where do they stop working?

level: middleimportance: should knowfreq 30%

basics

~10 s

A higher-order message is a shortcut where a collection method name, used as a property, forwards the next property read or method call to every item: $responses->sum->score or $responses->each->markReviewed().

open as a page

In Laravel, which collection methods such as transform() and push() change the collection in place, and what bugs does that cause in shared code?

level: seniorimportance: should knowfreq 35%

basics

~10 s

transform(), push(), put(), prepend(), forget(), pop(), shift(), pull() and splice() modify the collection itself. Because collections are objects passed by handle, a helper calling them changes the caller's data too.

open as a page

In Laravel, how do you add a custom method to every collection with Collection::macro(), and where should the macro be registered?

level: middleimportance: nice to knowfreq 25%

basics

~20 s

Call Collection::macro('name', fn) with a closure in a service provider's boot() method; the Macroable trait binds the closure to the collection, so $this inside it is the instance and the method works on every Collection, including Eloquent ones.

open as a page