Sorting Laravel Collection in a custom order
Tags: laravel, customsort, algorithms, mallowtech
Published: 2023-03-03 10:43:07

Currently, Laravel doesn't provide an option to sort the collection in a custom order.

We found some methods to sort the collection.

## Method 1 :
We found an algorithm to sort the collection in custom order.

The algorithm takes an array of input IDs that need to be sorted as the first parameter and an array consisting of elements the original array needs to be sorted in.

The algorithm then returns the sorted array of IDs. We then have to push the original collection in the sorted order.

```php
class CustomOrderSorting
{
    protected $A1, $A2, $m, $n;

    public function __construct($A1, $A2)
    {
        $this->A1 = &$A1;
        $this->A2 = &$A2;
        $this->m = count($A1);
        $this->n = count($A2);
    }

    /**
     * @param $arr
     * @param $low
     * @param $high
     * @param $x
     * @param $n
     * @return int
     */
    protected function first(&$arr, $low, $high, $x, $n)
    {
        if ($high >= $low) {
            $mid = (int)($low + ($high - $low) / 2);
            if (($mid == 0 || $x > $arr[$mid - 1]) &&
                $arr[$mid] == $x)
                return $mid;
            if ($x > $arr[$mid])
                return $this->first($arr, ($mid + 1), $high, $x, $n);
            return $this->first($arr, $low, ($mid - 1), $x, $n);
        }
        return -1;
    }

    /**
     * Sort A1[0..m-1] according to the order
     * defined by A2[0..n-1].
     * @param $A1
     * @param $A2
     * @param $m
     * @param $n
     * @return void
     */
    public function sortAccording(): array
    {
        // The temp array is used to store a copy
        // of A1[] and visited[] is used mark the
        // visited elements in temp[].
        $temp = array_fill(0, $this->m, NULL);
        $visited = array_fill(0, $this->m, NULL);
        for ($i = 0; $i < $this->m; $i++) {
            $temp[$i] = $this->A1[$i];
            $visited[$i] = 0;
        }

        // Sort elements in temp
        sort($temp);

        $ind = 0; // for index of output which is sorted A1[]

        // Consider all elements of A2[], find
        // them in temp[] and copy to A1[] in order.
        for ($i = 0; $i < $this->n; $i++) {
            // Find index of the first occurrence
            // of A2[i] in temp
            $f = $this->first($temp, 0, $this->m - 1, $this->A2[$i], $this->m);

            // If not present, no need to proceed
            if ($f == -1) continue;

            // Copy all occurrences of A2[i] to A1[]
            for ($j = $f; ($j < $this->m &&
                $temp[$j] == $this->A2[$i]); $j++) {
                $this->A1[$ind++] = $temp[$j];
                $visited[$j] = 1;
            }
        }

        // Now copy all items of temp[] which
        // are not present in A2[]
        for ($i = 0; $i < $this->m; $i++) {
            if ($visited[$i] == 0)
                $this->A1[$ind++] = $temp[$i];
        }
        return $this->A1;
    }
}
```

How to use the Custom order sorting algorithm : 
For demonstration, I will be using the User model and retrieving all the entries from the table.

```php
$sortOrder = [187, 411, 473, 492, 561, 834, 743, 123];

$users = User::get();

$userKeyByID = $users->keyBy('id'); // Set the individual elements key by the id field
$usersIDKeys = $userKeyByID->keys()->toArray(); // Get the keys (which is id)

$customSortOrder = (new CustomOrderSorting($usersIDKeys, $sortOrder))->sortAccording();

$sortedUsers = collect();

foreach ($customSortOrder as $id) {
    if (isset($userKeyByID[$id])) {
        $sortedUsers->push($userKeyByID[$id]);
    }
}
```

## Method 2 :
Laravel doesn't provide a way to custom sort the collection but it does provide a way to sort the collection in any order  using 

```php
$collection->sortBy($callback, $options = SORT_REGULAR, $descending = false)
```

We can use this method to sort the collection in a custom order.

```php
$users = User::get();

$sortOrder = array_flip([187, 411, 473, 492, 561, 834, 743, 123]);

$sortedUser = $users->sortBy(function ($user, $key) use ($sortOrder) {
    return $sortOrder[$user->id] ?? 999999;
})->values();
```

In this method, we will return the position of the element to sort the order. If the element's id is not in the sorting array, we will return a large value to denote that the value should be listed at last.

## Conclusion :
We ran both the methods and took the average time taken to process 500 records.

### Results :
Algorithm (method 1) took around `0.02082` seconds.
SortBy (method 2) took around `0.00938` seconds.

Algorithm took nearly `121.96%` more time than the conventional `sortBy()` method.