Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

fikrifadillah's avatar

Collection: shift() after filter()

Can't we use shift() after filter()?

As an example, I have code like this:

$accounts = Account::with('server')->get();
$channels = $accounts->pluck('server.channel')->values()->unique()->values()->toArray();

$total = 120;

$container = collect();

while ($total > 0) {
	foreach ($channels as $channel) {
		if ($total === 0) break;

		$container->push(
			$accounts->filter(fn ($account) => $account->server->channel === $channel)->values()->shift();
		);

		$total--;
	}
}

return $container;

The issue is I got duplicated data. I think shift() cannot be used like that. Any suggestions?

0 likes
2 replies
tisuchi's avatar

@fikrifadillah Try this:


$accounts = Account::with('server')->get();
$channels = $accounts->pluck('server.channel')->values()->unique()->values()->toArray();

$total = 120;

$container = collect();

while ($total > 0 && $accounts->isNotEmpty()) {
    foreach ($channels as $channel) {
        if ($total === 0) break;

        $filteredAccounts = $accounts->filter(fn ($account) => $account->server->channel === $channel);

        if ($filteredAccount = $filteredAccounts->first()) {
            $key = $accounts->search($filteredAccount);
            $container->push($filteredAccount);
            $accounts->forget($key);
            $total--;
        }
    }
}

return $container;
1 like
fikrifadillah's avatar

@tisuchi Thank you for the reply. But, I am afraid I can't use your suggestion because using search() and then extra line to forget() inside the loop. I am afraid because of them my code goes slow.

But, I figured it out to keep using filter() then shift() like this:

$accounts = Account::with('server')->get();

// Refactor this
$channels = $accounts->pluck('server.channel')->values()->unique()->values()->toArray()
	>map(function ($channel) use ($accounts) {
		return [
			'channel' => $channel,
			'accounts' => $accounts->filter(function ($account) use ($channel) {
				return $account->server->channel === $channel;
			})->values()
		];
	});

$total = 120;

$container = collect();

while ($total > 0) {
	foreach ($channels as $channel) {
		if ($total === 0) break;

		// Refactor this
		if ($channel['accounts']->isNotEmpty()) {
			$container->push(
				$container['accounts']->shift();
			);

		}
		
		$total--;
	}
}

return $container;

But, I need the answer why using another \Illuminate\Support\Collection methods before shift() keep resetting the Collection? I have try using another code like this:

$numbers = collect()->range(1, 10)->values();

$container = collect();

$index = 0;

while ($index < 6) {
	if ($numbers->isNotEmpty()) {
		$container->push($numbers->shuffle()->shift());
	}
	
	$index++;
}

return [$numbers->count(), $container->count()];

And then the result is [10, 6]. I assume using another \Illuminate\Support\Collection methods before shift() keep resetting the $numbers Collection.

1 like

Please or to participate in this conversation.