Collection::intersect

Removes any values not present in the given array or collection, keeping the original keys.

Signature
Collection::intersect(Arrayable|iterable $items): static

Parameters

ParameterTypeDescription
$items\Illuminate\Contracts\Support\Arrayable<TKey, TValue>|iterable<TKey, TValue>The values to intersect against. Arrays, collections and any Arrayable are accepted; only the values are considered, never the keys.

Return value

staticA new collection holding only the values that also appear in the given items, each keeping the key it had in the original collection.

intersect keeps only the values that also appear in the collection or array you pass it. Everything else is dropped. It is the collection wrapper around PHP's array_intersect, and it inherits that function's semantics exactly.

When to reach for it

Use it to constrain a list against a set of allowed values: filtering requested fields down to the ones you actually expose, narrowing a user's requested permissions to those they hold, or finding what two already-loaded lists have in common. Order and keys always come from the receiver, so the argument acts purely as a whitelist.

The thing that trips people up

The comparison is done on the string form of each value. collect([1, 2, 3])->intersect(['1', '3']) keeps the integers 1 and 3, because (string) 1 === '1'. That is convenient for ids arriving from a query string, and a source of false positives when a collection mixes types. Objects without a __toString will raise a conversion error rather than compare by identity.

Keys are preserved from the original collection, so the result of intersecting a list is a list with gaps — the same JSON-shape trap that filter has. Chain values() before serialising. To intersect on keys instead of values, use intersectByKeys; for custom comparison, intersectUsing.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Narrow a client's requested field list down to the columns you actually allow.
  • Find the permissions shared between a user and a role without another query.
  • Check whether two already-loaded lists overlap at all before doing more work.

Frequently asked questions

Does intersect compare values strictly?

No. It uses array_intersect, which compares the string form of each value, so the integer 1 matches the string '1'. Use filter with a strict in_array check when types must match exactly.

Why does my intersected collection have gaps in its keys?

Because intersect preserves the keys the surviving values had in the original collection. Chain values() to re-index before serialising, or a JSON array turns into an object.

What is the difference between intersect and intersectByKeys?

intersect matches on values and returns the values common to both; intersectByKeys matches on keys and returns the receiver's entries whose keys appear in the given items, regardless of what the values are.

More examples

intersect compares values, intersectByKeys compares keys

The first output is [2 => 'Chair'] — note the key 2 carried over from the original position. The second is ['type' => 'screen']: intersectByKeys matched on the key type and kept the receiver's value, completely ignoring that the values differ. Picking the wrong one of these two silently returns plausible but incorrect data.

Example · php
<?php
$a = collect(['Desk', 'Sofa', 'Chair']);
print_r($a->intersect(['Chair', 'Bookcase'])->all());

$b = collect(['serial' => 'UX301', 'type' => 'screen', 'year' => 2009]);
print_r($b->intersectByKeys(['reference' => 'UX404', 'type' => 'tab'])->all());

Constraining requested fields to an allowlist

The output is the list id, name, email followed by "id, name, email". password_hash is dropped because it is not in the allowlist, and created_at does not appear because intersect only ever returns values from the receiver. The values() call matters here: without it the keys would be 0, 1, 3 and the JSON form would be an object.

Example · php
<?php
$requested = collect(['id', 'name', 'password_hash', 'email']);

$allowed = ['id', 'name', 'email', 'created_at'];

$safe = $requested->intersect($allowed)->values();

print_r($safe->all());
echo $safe->implode(', '), PHP_EOL;

String coercion and its false positives

The first output is [10, 30], which is exactly the behaviour you want for route or query parameters. The second keeps both the integer 1 and the boolean true, since both cast to the string "1". When the distinction matters, drop to filter with a strict in_array rather than relying on intersect.

Example · php
<?php
// Useful: ids from a query string match integer ids.
print_r(collect([10, 20, 30])->intersect(['10', '30'])->values()->all());

// Surprising: true and 1 both stringify to "1".
print_r(collect([1, true, 'x'])->intersect(['1'])->all());

// Safer when types matter - compare explicitly.
$allowed = [10, 30];
print_r(collect([10, 20, 30])->filter(fn ($v) => in_array($v, $allowed, true))->values()->all());

Discussion

  • Be the first to comment on this lesson.