Simon Kollross

Simon Kollross

Software developer, blogger, elegant code enthusiast.

A join collection macro

Today I needed to join two Laravel collections like a SQL join would do. The collection zip function was not what I needed, because it joins only on indexes, not on values. My macro should join two collections based on a custom join condition given by a callback.

Collection::macro('join', function ($items, callable $callback) {
    return $this->flatMap(function ($value) use ($items, $callback) {
        return $items->filter(function ($item) use ($value, $callback) {
            return $callback($value, $item);
        })->map(function ($item) use ($value) {
            return new Collection([$value, $item]);
        });
    });
});

This macro function takes the actual collection, which is the base for the join and another collection to join with. The filter function filters the second collection for items, which satisfy the given join condition callback. The filtered collection items are mapped to a new collection, which contains the joined pair. With the outer map function the results are collected and at last flattened one level.

$first = collect([0, 1, 2]);
$second = collect([0, 0, 1]);
$result = $first->join($second, function ($a, $b) {
    return $a == $b;
});
dd($result);

And here is the result:

Joined collection

This is a very clean and reusable functional solution in comparison to writing an imperative nested loop join over and over again from scratch.