Howard
Howard

Reputation: 3758

Elegant solution to reordering a Laravel Collection (or populate it perfectly from the beginning)?

I have a Laravel Collection with items in it. Assuming the items are in the collection in numerical order.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

If for example I have items 1-15 laid out like the following in the View (the number of rows can change but number of columns will always stay the same):

1  4  7  10  13
2  5  8  11  14
3  6  9  12  15

*think of each column as a different type of mammal (cats, dogs, birds, etc.)

Is there a way to easily reorder the items like the following within the Collection?

1 4 7 10 13 2 5 8 11 14 3 6 9 12 15

So in the View it would now look like this:

1   2   3
4   5   6
7   8   9
10  11  12
13  14  15

*The View that I talk about is for illustrative purposes only. I only require changes to the Collection in the manner stated above. Populating the collection perfectly from the beginning would be the optimal answer but I can't figure out a way to do so.

Upvotes: 2

Views: 8226

Answers (4)

Jarek Tkaczyk
Jarek Tkaczyk

Reputation: 81187

While not discussing whether it makes sense to order the collection or better do it in your SQL query, this is the answer for your generic question (adjust for your specific needs if necessary):

1 compare function:

/**
 * Sort the array in groups.
 * 
 * @param  mixed    $previous
 * @param  mixed    $next
 * @param  integer  $groups
 * @return bool
 */
function sortInGroups($previous, $next, $groups = 3)
{
    // If equal then return 0
    if ($previous == $next) return 0;

    // Otherwise check the modulus in order to 
    // group the items. However if modulus is zero, 
    // then shift group to the very end of final array.
    $prev = ($previous%$groups) ?: $groups;
    $nxt = ($next%$groups) ?: $groups;

    // If moduli are equal then items are 
    // in the same group so compare the items.
    if ($prev == $nxt) 
    {
        return ($previous < $next) ? -1 : 1;
    }

    // Otherwise compare the moduli in order to group the items.
    return ($prev < $nxt) ? -1 : 1;
}

2 sort the collection by whatever property you want (id for example):

$collection = SomeModel::take(15)->get(); // or Support\Collection 
$collection->lists('id'); // [1,2,3,4,... 15]

$collection->sort(function ($prev, $next) {
  return sortInGroups($prev->id, $next->id, $cols = 5);
});

$collection->lists('id'); // [1,4,7,10,13,2,5,8,11,14,3,6,9,12,15]

Upvotes: 1

lukasgeiter
lukasgeiter

Reputation: 152900

I don't think it is perfect but it works.

This code assumes $collection is the collection you want to reorder

$arrayCollection = array();

$numberOfColumns = 5; // adjust this value to change number of columns
$numberOfRows = $collection->count() / $numberOfColumns

foreach($collection as $index => $model){
    $column = floor($index / $numberOfRows);
    $newIndex = (int)($index % $numberOfRows) * $numberOfColumns + $column;
    $arrayCollection[$newIndex] = $model;
}

ksort($arrayCollection); // sort collection by key

$collection = new Collection($arrayCollection); // make new collection with reordered models

Upvotes: 2

NULL
NULL

Reputation: 1858

You can manually order by a column. If these are the id 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Then you can use order by FIELD(id, 1, 4, 7, 10, 13, 2, 5, 8, 11, 14, 3, 6, 9, 12, 15);

Upvotes: 0

Jeff Lambert
Jeff Lambert

Reputation: 24661

Why should you reorder the collection? Sounds to me you want something like this:

@foreach($collection as $item)
    <div class="collection-item">{{ $item->value }}</div>
@endforeach

Then apply CSS styles in order to have each of the collection-item divs display inline rather than as block elements.

If you truly want a reordered collection, then specify the order you want when you fill the collection:

Model::all()->orderBy('field', 'desc')->get();

Upvotes: 1

Related Questions