Simon Kollross

Simon Kollross

Software developer, blogger, elegant code enthusiast.

A repeat collection macro

A short time ago I had to add a placeholder item to a Laravel collection several times. I created a repeat macro to achieve this without breaking up the collection pipeline flow.

Collection::macro('repeat', function ($times, callable $callback) {
    for ($i = 0; $i < $times; $i++) {
        $callback($this);
    }
    return $this;
});

This was the first version and it allows you to write things like this:

collect([1, 2, 3])->repeat(3, function ($collection) {
    $collection->prepend(null);
});

This adds null 3 times to the head of the collection: [null, null, null, 1, 2, 3]. This implementation was sufficient for my needs, but I wanted to generalize it a bit to allow more flexibility.

Collection::macro('repeat', function ($times, callable $callback) {
    if (is_callable($times)) {
        $i = 0;
        while ($times($this)) {
            $callback($this, $i);
            $i++;
        }
        return $this;
   }
    for ($i = 0; $i < $times; $i++) {
        $callback($this, $i);
    }
    return $this;
});

Now $times can be a callback which should return a boolean value. The abort condition can be specified based on the collection’s state. Every time $callback is executed, $i is passed to have access to the current iteration index.

collect([1, 2, 3])->repeat(function ($collection) {
    return $collection->count() < 10;
}, function ($collection, $i) {
    $collection->push($i);
});

So as long as the collection contains less than 10 items, new items are pushed to the collection.

You can use the pipe() collection method for this task, too. But I wanted to have a short and elegant way to solve this problem without having to write a loop manually. If you like programming with collection pipelines, I hope you’ll enjoy this macro.