fishmong3r
fishmong3r

Reputation: 1434

Dynamic count based on value

So, I have a two-dimensional array:

$fruits = array
  (
  array('fruit' => 'Apple', 'rating' => '2'),
  array('fruit' => 'Pear', 'rating' => '3'),
  array('fruit' => 'Peach', 'rating' => '2'),
  array('fruit' => 'Strawberry', 'rating' => '2')
  );

I need to get an explicit order. Therefore, taking the example above, I want to display a table containing three cols -> fruit, rating and rank. If there is any equality in the ratings then I want to have radio buttons in the third col with the possible ranks. This is what I mean:

enter image description here

So far I have the below piece of code which is only capable to tell whether there is another fruit with the same rating, and if yes it shows radios from 1-4. This is a very rude solution. So how can I easily build it up taking the fact into consideration that the rating can be anything. For e.g. 2-2-3-3 in which case I need radios 1-2,1-2 and 3-4,3-4.

PHP:

echo "<table><tr><td>fruit</td><td>rating</td><td>rank</td></tr>";
for($i = 0; $i < count($fruits); ++$i) {
    echo "<tr><td>".$fruits[$i]['fruit']."</td><td>".$fruits[$i]['rating']."</td><td>";    
    for($j = 0; $j < count($fruits); ++$j) {
        if($i != $j) {  
            if($fruits[$i]['rating'] == $fruits[$j]['rating']) {
                echo "<input type=\"radio\" name=\"".$fruits[$i]['fruit']."\" value=\"1\">1
                <input type=\"radio\" name=\"".$fruits[$i]['fruit']."\" value=\"2\">2
                <input type=\"radio\" name=\"".$fruits[$i]['fruit']."\" value=\"3\">3
                <input type=\"radio\" name=\"".$fruits[$i]['fruit']."\" value=\"4\">4"
                break;
            }
            else if ($j == 3)
            {
                echo "<b>".($i+1)."</b></td></tr>";
            }
        }
        else if ($j==3)
        {
            echo "<b>".($i+1)."</b></td></tr>";
        }
    }
}
echo "</table>";

Basically I need to have a count per rating for all the cases there is an equality.

P.S.: The array is already DESC sorted based on the ratings, this is why ($i+1) works in my code.

Upvotes: 1

Views: 259

Answers (3)

Niet the Dark Absol
Niet the Dark Absol

Reputation: 324690

Since you already have a pre-sorted array, what you need is to find ranges.

Once you have the range, you can then output that set of rows.

Here's an example:

echo "<table><tr><td>fruit</td><td>rating</td><td>rank</td></tr>";
$total = count($fruits);
for( $i=0; $i<$total; $i=$range_end+1) {
    $range_start = $i;
    while($i+1 < $total && $fruits[$i]['rating'] == $fruits[$i+1]['rating']) {
        $i++;
    }
    $range_end = $i;

    $range = array_slice($fruits, $range_start, $range_end-$range_start+1);
    foreach($range as $fruit) {
        echo "<tr>"
            ."<td>".$fruit['fruit']."</td>"
            ."<td>".$fruit['rating']."</td>"
            ."<td>";
        if( $range_start == $range_end) {
            echo $fruit['rating'];
        }
        else {
            for($j=$range_start; $j<=$range_end; $j++) {
                echo '<input type="radio" name="'.$fruit['fruit'].'" value="'.$j.'">'.$j;
            }
        }
        echo "</td>"
        ."</tr>";
    }
}
echo "</table>";

Note the slightly unconventional for $i loop. What's happening here is that we're starting the loop at 0, recording the position we are at in $range_start, then advancing until the next fruit's rating is different. At that point we have $range_end, which may be the same as $range_start if there are no fruits with the same rating. Then we extract the range from the $fruits array and output it. Once that is done, the loop continues one place past the end of the range we just did.

Upvotes: 1

Tiago Luz
Tiago Luz

Reputation: 129

Take a look on usort(). This can re-order your array (or clone it) based on a custom function.

usort

Upvotes: 0

trincot
trincot

Reputation: 350365

Here is what I would suggest:

$fruits = array(
  array('fruit' => 'Apple', 'rating' => '2'),
  array('fruit' => 'Pear', 'rating' => '3'),
  array('fruit' => 'Peach', 'rating' => '2'),
  array('fruit' => 'Strawberry', 'rating' => '2')
);

// get unique ratings, in descending order
$ratings = array_unique(array_column($fruits, 'rating'));
sort($ratings);
$ratings = array_fill_keys(array_reverse($ratings), array());

// put fruits in their respective rating buckets:
foreach($fruits as $fruit) {
    $ratings[$fruit['rating']][] = $fruit['fruit'];
}

// Generate the HTML table
$html = "<table border=1><tr><th>fruit</th><th>rating</th><th>rank</th></tr>";
$rank = 1;
foreach($ratings as $rating => $fruits) {
    foreach($fruits as $fruit) {
        if (count($fruits) === 1) {
            $rankHtml = "<b>$rank</b>";
        } else {
            $rankHtml = "";
            for ($i = $rank; $i < $rank + count($fruits); $i++) {
                $rankHtml .= "<input type='radio' name='$fruit' value='$i'>$i";
            }
        }
        $html .= "<tr><td>$fruit</td><td>$rating</td><td>$rankHtml</td></tr>\n";
    }
    $rank += count($fruits);
}
$html .= "</table>";

echo $html;

As you can see I like to separate the logic from the output. The only echo is at the end.

The main characteristic of this method is the intermediate structure, where the fruit names are grouped into separate arrays depending on their common rating. This, I think, results in nicer code.

Upvotes: 0

Related Questions