Skip to content
On this page

Laravel Collections: Why You Shouldn’t Forget ->values()

Sorting in Laravel works great — until your sorted data shows up in the wrong order on the frontend.

The Problem

Given a sorted collection:

php
$expertiseOptions = collect([
                        ['id' => 5, 'label' => 'Banana'],
                        ['id' => 2, 'label' => 'Apple'],
                        ['id' => 11, 'label' => 'Grape'],
                        ['id' => 1, 'label' => 'Cherry'],
                        ['id' => 21, 'label' => 'Mango'],
                    ])
                    ->sortBy('label')
                    ->toArray();

You’d expect the outcome:

php
[
  0 =>  ["id" => 2, "label" => "Apple"],
  1 =>  ["id" => 5, "label" => "Banana"],
  2 =>  ["id" => 1, "label" => "Cherry"],
  3 =>  ["id" => 11, "label" => "Grape"],
  4 =>  ["id" => 21, "label" => "Mango"]
]

However, if you don’t call values(), the keys are not reindexed. Instead, the collection retains its original keys:

php
[
  1 =>  ["id" => 2, "label" => "Apple"],
  0 =>  ["id" => 5, "label" => "Banana"],
  3 =>  ["id" => 1, "label" => "Cherry"],
  2 =>  ["id" => 11, "label" => "Grape"],
  4 =>  ["id" => 21, "label" => "Mango"]
]

In PHP, arrays can have non-sequential keys, such as 1, 2, 4, 5, rather than strictly 0, 1, 2, 3, .... In many programming languages, this concept is referred to as a hashmap rather than an array. For example, in JavaScript, a collection is only considered an array if its keys are consecutive integers starting from 0. Otherwise, it is treated as an object. This difference in interpretation is what causes the sorting issue. Let’s dive into the next section for a more detailed explanation.

Example JSON without values():

This collection is converted to a JSON object (which happens behind the scenes if you use Inertia.js), because the index values are not in sequence. It might look like this:

json
{
  "1": { "id": 2, "label": "Apple" },
  "0": { "id": 5, "label": "Banana" },
  "3": { "id": 1, "label": "Cherry" },
  "2": { "id": 11, "label": "Grape" },
  "4": { "id": 21, "label": "Mango" }
}

Since the JSON data is treated as an object with non-sequential keys, Vue will iterate over these keys in an unexpected order:

vue
<div v-for="item in expertiseOptions" :key="item.id">
  {{ item.label }}
</div>

Given the above JSON, Vue will render the items in the following order:

Banana, Apple, Grape, Cherry, Mango

This happens because JavaScript sorts object keys (which are treated as strings) in a specific order when iterating over them. It treats "1", "0", "3", etc., as strings, leading to a lexicographic sort that may differ from the intended numeric order. If you had number higher then 10, the sorting would be 1,11,12,13,14,2,21,23..

The Fix: Use ->values()

php
$expertiseOptions = collect([
                        ['id' => 5, 'label' => 'Banana'],
                        ['id' => 2, 'label' => 'Apple'],
                        ['id' => 11, 'label' => 'Grape'],
                        ['id' => 1, 'label' => 'Cherry'],
                        ['id' => 21, 'label' => 'Mango'],
                    ])
                    ->sortBy('label')
                    ->values()
                    ->toArray();

By using values(), Laravel reindexes the collection to have sequential keys (0, 1, 2,...).

php
[
  0 =>  ["id" => 2, "label" => "Apple"],
  1 =>  ["id" => 5, "label" => "Banana"],
  2 =>  ["id" => 1, "label" => "Cherry"],
  3 =>  ["id" => 11, "label" => "Grape"],
  4 =>  ["id" => 21, "label" => "Mango"]
]

Now, the JSON output will be an array, and no longer an object:

json
[
  { "id": 2, "label": "Apple" },
  { "id": 5, "label": "Banana" },
  { "id": 1, "label": "Cherry" },
  { "id": 11, "label": "Grape" },
  { "id": 21, "label": "Mango" }
]

In this case, the JSON is treated as an array by JavaScript, which guarantees the order of elements. When Vue iterates over this data, it will preserve the order as intended:

Conclusion

Always use ->values() after ->sortBy() if you pass collections to the frontend.
It ensures sequential keys, consistent JSON output, and correct rendering order in Vue.

Note: When using Inertia.js, you can pass a Laravel collection directly to Vue — it will be automatically converted to an array (if you used values()). There's no need to call toArray(). I only used it here for clarity in the examples.