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

eggplantSword's avatar

Save index to collection after grouping and on a each

I'm trying to take a collection of items, group them and create a new collection that has the index since I need the index for my view.

Right now it's saving them wrong like [1,2,4] or [0,0]. This is my code

$group = $prods->groupBy('product_id');

$products = collect();

$group->each(function ($tot, $key) use ($products) {
    if (count($tot) === 1) {
        $products->push(
            [
                'product_id' => $tot[0]['product_id'],
                'product' => $tot[0]['product'],
                'index' => $key,
                'quantity_client' => $tot[0]['quantity_client'],
                'quantity_seller' => $tot[0]['quantity_seller']
            ]
        );
    } else {
        $quantity_client = 0;
        $quantity_seller = 0;

        $tot->each(function ($value) use (&$quantity_client, &$quantity_seller) {
            $quantity_client += $value['quantity_client'];
            $quantity_seller += $value['quantity_seller'];
        });

        $products->push(
            [
                'product_id' => $tot[0]['product_id'],
                'product' => $tot[0]['product'],
                'index' => $key,
                'quantity_client' => $quantity_client,
                'quantity_seller' => $quantity_seller
            ]
        );
    }
});

return $products;

What am I doing wrong?

0 likes
1 reply
rodrigo.pedra's avatar

Not sure I understand what you need, but I think you want each group index to be 0-based.

Problem is when you call:

$group = $prods->groupBy('product_id');

The resulting collection will be indexed by each group's product_id and not by 0-based increasing indices.

So when you call $group->each(function ($tot, $key) use ($products) { the $key will match each group's product_id.

If you want to reset those indices you can call ->values() after grouping them by:

// EDIT: added missing ->values() call
$group = $prods->groupBy('product_id')->values();

But, as a suggestion, I think you could simplify your code to:

// sample data
$prods = collect([
    ['product_id' => 1, 'product' => 'Product 1', 'quantity_client' => 11, 'quantity_seller' => 15],
    ['product_id' => 1, 'product' => 'Product 1', 'quantity_client' => 12, 'quantity_seller' => 16],
    ['product_id' => 1, 'product' => 'Product 1', 'quantity_client' => 13, 'quantity_seller' => 17],
    ['product_id' => 2, 'product' => 'Product 2', 'quantity_client' => 21, 'quantity_seller' => 25],
    ['product_id' => 2, 'product' => 'Product 2', 'quantity_client' => 22, 'quantity_seller' => 26],
    ['product_id' => 2, 'product' => 'Product 2', 'quantity_client' => 23, 'quantity_seller' => 27],
]);

$products = $prods
    ->groupBy('product_id')
    ->values() // reset groups' indices
    ->map(function ($group, $index) {
        return [
            'product_id' => $group[0]['product_id'],
            'product' => $group[0]['product'],
            'index' => $index,
            'quantity_client' => $group->sum('quantity_client'),
            'quantity_seller' => $group->sum('quantity_seller'),
        ];
    });

return $products;

If you really think calling ->sum(...) twice is a no-go as it would iterate each group twice you can try using ->reduce(...)

$prods = collect([
    ['product_id' => 1, 'product' => 'Product 1', 'quantity_client' => 11, 'quantity_seller' => 15],
    ['product_id' => 1, 'product' => 'Product 1', 'quantity_client' => 12, 'quantity_seller' => 16],
    ['product_id' => 1, 'product' => 'Product 1', 'quantity_client' => 13, 'quantity_seller' => 17],
    ['product_id' => 2, 'product' => 'Product 2', 'quantity_client' => 21, 'quantity_seller' => 25],
    ['product_id' => 2, 'product' => 'Product 2', 'quantity_client' => 22, 'quantity_seller' => 26],
    ['product_id' => 2, 'product' => 'Product 2', 'quantity_client' => 23, 'quantity_seller' => 27],
]);

$products = $prods
    ->groupBy('product_id')
    ->values() // reset indices
    ->map(function ($group, $index) {
        $values = $group->reduce(function ($values, $record) {
            $values['quantity_client'] += $record['quantity_client'] ?? 0;
            $values['quantity_seller'] += $record['quantity_seller'] ?? 0;

            return $values;
        }, ['quantity_client' => 0, 'quantity_seller' => 0]);

        return [
            'product_id' => $group[0]['product_id'],
            'product' => $group[0]['product'],
            'index' => $index,
            'quantity_client' => $values['quantity_client'],
            'quantity_seller' => $values['quantity_seller'],
        ];
    });

return $products;

I personally don't think the performance impact is noticeable, unless you have a really huge dataset.

Hope it helps.

Please or to participate in this conversation.