user1251698
user1251698

Reputation: 2143

PHP: group items a loop

The following loop groups the items into two groups: First group of 5 items in a li and then each group of 6 items. I'm trying to modify this to group first 3 items in a li and then next each 5 items in the li.

for ($i = 1; $i < 20; $i++) {
    echo ($i === 1 || $i % 6 === 0) ? "<li>" : null,
        "<div>item {$i}</div>",
        ($i % 6 === 5) ? "</li>" : null;
}
if ($i % 6 !== 0) echo "</li>";

I'm trying to modify to following, but the grouping does not work fine (eg. wrong number of items goes in a li, some items do not get in li etc.)

for ($i = 1; $i < 20; $i++) {
    echo ($i === 1 || $i % 5 === 0) ? "<li>" : null,
        "<div>item {$i}</div>",
        ($i % 5 === 3) ? "</li>" : null;
}
if ($i % 5 !== 0) echo "</li>";

Upvotes: 0

Views: 1433

Answers (2)

irrelephant
irrelephant

Reputation: 4111

Try this (Edited):

echo "<li>";
for($i = 1; $i < 20; $i++) {
    $mod = $i % 5;
    echo $mod == 4 ? "<li>" : null,
        "<div>item {$i}</div>",
        $mod == 3 ? "</li>" : null;
}
if($i % 5 != 3) echo "</li>";

Code example with slight changes

Explanation:

If you want to group items in groups of 5, then mod by 5. The first three items are a special case, but since 3 is less than 5, we can incorporate the first three numbers into our main loop.

The problem with your code is in adding the <li>s. Since we start $i at 1 instead of 0, we should use $i % 5 == 4 instead of $i % 5 == 0.

Upvotes: 1

newfurniturey
newfurniturey

Reputation: 38456

If I understand your desired output, you want something like (ignoring <div> tags for brevity):

<li>item #1</li>
<li>item #2</li>
<li>item #3</li>
<li>
    item #4
    item #5
    item #6
    item #7
    item #8
</li>
... repeat

If this is correct, and the numbers of items in each "rule" (rule being "first three" and "next five") could change (as they just did), I would recommend created variables to represent them - that way, in the future, you only have to change the variables and not worry about making actual code-changes.

The first method I'd recommend is a (semi)-full rewrite. This method uses a bunch of if-statements in a way that I feel is more readable/manageable:

$firstGroupLength = 3;
$repeatedGroupLength = 5;

for ($i = 1; $i < 20; $i++) {
    if ($i <= $firstGroupLength) {
        echo '<li><div>item ' . $i . '</div></li>';
    } else {
        if ((($i - $firstGroupLength) % $repeatedGroupLength) == 1) {
            echo '<li>';
        }
        echo '<div>item ' . $i . '</div>';
        if ((($i - $firstGroupLength) % $repeatedGroupLength) == 0) {
            echo '</li>';
        }
    }
}
if ((($i - $firstGroupLength) % $repeatedGroupLength) != 1) {
    echo '</li>';
}

This method will get the job done, but is also a big on the longer side. However, if at any time in the future you want the first group to appear differently than the repeating group, say maybe with different a CSS class or some other random feature - this method lays it out on the table for you!

If you'd prefer to stick with your ternary operator method, the following should work just as well:

$firstGroupLength = 3;
$repeatedGroupLength = 5;

echo '<li>';
for($i = 1; $i < 20; $i++) {

    echo ((($i % $repeatedGroupLength) == ($repeatedGroupLength - 1)) ? '<li>' : '')
        . '<div>item ' . $i . '</div>'
        . ((($i % $repeatedGroupLength) == $firstGroupLength) ? '</li>' : '');
}
if (($i % $repeatedGroupLength) != $firstGroupLength) echo '</li>';

In both of these methods, you will find a $firstGroupLength variable. This is set to the first "group" of items that repeat by themselves (such as <li>item #</li>). The second variable, $repeatedGroupLength will set the number of elements that display in the repeating "item groups" blocks (such as <li>item # item # item #</li>).

Upvotes: 1

Related Questions