对于这种情况,阵列是否比交换机情况更有效?

I'm displaying star ratings based on input from an API (note: only displaying, not receiving ratings at all). The following code works exactly as I need it to, but it's a very large piece of logic for something that feels like it could be massively simplified.

I read a few other tickets here that suggest that using an array might be more effective than a switch case. But because the logic depends on each case between true within a range of numbers (like more than 2.5 but less than 3) I'm not sure an array would work at all in this case.

Bottom line is this: Can this code be massively simplified somehow?

$stars = 3.5;

switch ($stars) {
  case ($stars > 1 && $stars <= 1.5):
    $star2 = 'dashicons-star-half';
    $star3 = 'dashicons-star-empty';
    $star4 = 'dashicons-star-empty';
    $star5 = 'dashicons-star-empty';
    break;
  case($stars > 1.5 && $stars <= 2):
    $star2 = 'dashicons-star-filled';
    $star3 = 'dashicons-star-empty';
    $star4 = 'dashicons-star-empty';
    $star5 = 'dashicons-star-empty';
    break;
  case($stars > 2 && $stars <= 2.5):
    $star2 = 'dashicons-star-filled';
    $star3 = 'dashicons-star-half';
    $star4 = 'dashicons-star-empty';
    $star5 = 'dashicons-star-empty';
    break;
  case($stars > 2.5 && $stars <= 3):
    $star2 = 'dashicons-star-filled';
    $star3 = 'dashicons-star-filled';
    $star4 = 'dashicons-star-empty';
    $star5 = 'dashicons-star-empty';
    break;
  case($stars > 3 && $stars <= 3.5):
    $star2 = 'dashicons-star-filled';
    $star3 = 'dashicons-star-filled';
    $star4 = 'dashicons-star-half';
    $star5 = 'dashicons-star-empty';
    break;
  case($stars > 3.5 && $stars <= 4):
    $star2 = 'dashicons-star-filled';
    $star3 = 'dashicons-star-filled';
    $star4 = 'dashicons-star-filled';
    $star5 = 'dashicons-star-empty';
    break;
  case($stars > 4 && $stars <= 4.5):
    $star2 = 'dashicons-star-filled';
    $star3 = 'dashicons-star-filled';
    $star4 = 'dashicons-star-filled';
    $star5 = 'dashicons-star-half';
    break;
  case($stars > 4.5):
    $star2 = 'dashicons-star-filled';
    $star3 = 'dashicons-star-filled';
    $star4 = 'dashicons-star-filled';
    $star5 = 'dashicons-star-filled';
    break;
  default:
  $star2 = 'dashicons-star-empty';
  $star3 = 'dashicons-star-empty';
  $star4 = 'dashicons-star-empty';
  $star5 = 'dashicons-star-empty';
}

?>
 <div class="wporg-ratings" title="<?php echo $stars; ?> out of 5 stars" style="color:#e6b800;">
   <span class="dashicons dashicons-star-filled"></span>
   <span class="dashicons <?php echo $star2; ?>"></span>
   <span class="dashicons <?php echo $star3; ?>"></span>
   <span class="dashicons <?php echo $star4; ?>"></span>
   <span class="dashicons <?php echo $star5; ?>"></span>
 </div>

I don't think an array or a switch are needed. You can use a for loop, and check the $stars value against your loop variable to see which icon should be used.

<div class="wporg-ratings" title="<?php echo $stars; ?> out of 5 stars" style="color:#e6b800;">
<?php
// for loop: one iteration for each of five possible stars
// (for loops are generally best for repeating code a specific number of times)
for ($i=0; $i < 5; $i++) {
    if ($stars <= $i ) {
        // empty stars are displayed when the iterator (i) is >= the star value
        echo  '<span class="dashicons dashicons-star-empty"></span>';
    } elseif ($stars <= $i + 0.5) {
        // half stars are displayed for star values between i and i+0.5 
        echo  '<span class="dashicons dashicons-star-half"></span>';
    } else {
        // whole stars are displayed when the star value is > i+0.5
        echo  '<span class="dashicons dashicons-star-filled"></span>';
    }
}
?>
</div>

To help understand why this works, take a theoretical value through the loop. We can use the one from your question, 3.5.

  • first iteration: 3.5 > 0, 3.5 > 0.5, so you get the else value (filled star)
  • second iteration: 3.5 > 1, 3.5 > 1.5, so you get the else value (filled star)
  • third iteration: 3.5 > 2, 3.5 > 2.5, so you get the else value (filled star)
  • fourth iteration: 3.5 > 3, 3.5 = 3.5, so you get the elseif value (half star)
  • fifth iteration: 3.5 < 4, so you get the if value (empty star)

here is quick version:

$stars = 3.5;
$d = array(
    ($stars >= 1.0) ? 'dashicons-star-filled' : (($stars >= 0.5) ? 'dashicons-star-half' : 'dashicons-star-empty'),
    ($stars >= 2.0) ? 'dashicons-star-filled' : (($stars >= 1.5) ? 'dashicons-star-half' : 'dashicons-star-empty'),
    ($stars >= 3.0) ? 'dashicons-star-filled' : (($stars >= 2.5) ? 'dashicons-star-half' : 'dashicons-star-empty'),
    ($stars >= 4.0) ? 'dashicons-star-filled' : (($stars >= 3.5) ? 'dashicons-star-half' : 'dashicons-star-empty'),
    ($stars >= 5.0) ? 'dashicons-star-filled' : (($stars >= 4.5) ? 'dashicons-star-half' : 'dashicons-star-empty')
);
echo '<div class="wporg-ratings" title="' . $stars . ' out of 5 stars" style="color:#e6b800;">';
foreach ($d as $value) echo '<span class="dashicons ' . $value . '"></span>';
echo '</div>';

it is not the same as your original logic (when first star is always filled), but you can use it as direction

next step could be moving repeated code into function

Another possibility is to define an array of hashes. Let's say that that each element in the array contains "minimum value, and three strings," and that the array is sorted in ascending order. Your if/then logic could be replaced by a loop through that array, breaking-out as soon as it can.

However: "Clarity trumps all other concerns." If the code works and is reasonably maintainable, who cares if it stinks?

Your existing code could be simplified to eliminate the possibility of any value "falling through the cracks" by noticing that the first half of the if-condition can be omitted: if you reach the second case, it must be true that the value is greater than 1.5, and so on.