Convert user-entered value to local value

I create a metacritical type of site where users link peer review (from external sources).

In the form, I allow users to choose the type of rating that the source site uses:

rating

For example, a source message may have a rating of 50 out of 100 .

To display the rating on my site, I want to convert the source rating to a simple 5 star rating . Thus, the rating for the above example becomes 2.5

My question is, what would be the best way in PHP to do these types of calculations when considering performance and efficiency?

I try my best to find a good routine, especially with A+ , etc.

+5
source share
5 answers

This is an interesting question. Here is my naive attempt at a solution that is pretty mechanical, but trying to be fairly homogeneous. I am sure that it could be significantly improved / optimized.

Edit

I was not happy with my previous answer (I threw myself so that there were some errors - thanks for pointing them out!). I decided to remove this and try a different approach, which is a little crazy with regular expressions and filter_input , to determine if user input is valid - and if so - apply it to the appropriate type.

This makes checking the rating against the selected scale (by type and comparison of values) much more uniform. I have sensibly tested this a bit and I think I'm happier with this approach;)

Once again ... Assuming an HTML form, for example:

 <form method="post"> <label>rating</label> <input name="rating" type="text" autofocus> <label>out of</label> <select name="scale"> <option value="100">100</option> <option value="10">10</option> <option value="6">6</option> <option value="5">5</option> <option value="4">4</option> <option value="A">A</option> <option value="A+">A+</option> </select> <input type="submit"> </form> 

And the following PHP for user login:

 <?php const MAX_STARS = 5; const REGEX_RATING = '/^(?<char>[a-fA-F]{1}[-+]?)$|^(?<digit>[1-9]?[0-9](\.\d+)?|100)$/'; const REGEX_SCALE = '/^(?<char>A\+?)$|^(?<digit>100|10|6|5|4)$/'; $letters = [ 'F-', 'F', 'F+', 'G-', 'G', 'G+', 'D-', 'D', 'D+', 'C-', 'C', 'C+', 'B-', 'B', 'B+', 'A-', 'A', 'A+', ]; if ('POST' === $_SERVER['REQUEST_METHOD']) { // validate user-submitted `rating` $rating = filter_input(INPUT_POST, 'rating', FILTER_CALLBACK, [ 'options' => function($input) { if (preg_match(REGEX_RATING, $input, $matches)) { return isset($matches['digit']) ? (float) $matches['digit'] : strtoupper($matches['char']); } return false; // no match on regex }, ]); // validate user-submitted `scale` $scale = filter_input(INPUT_POST, 'scale', FILTER_CALLBACK, [ 'options' => function($input) { if (preg_match(REGEX_SCALE, $input, $matches)) { return isset($matches['digit']) ? (float) $matches['digit'] : strtoupper($matches['char']); } return false; // no match on regex } ]); // if a valid letter rating, convert to calculable values if (in_array($scale, ['A+', 'A']) && in_array($rating, $letters)) { $scale = array_search($scale, $letters); $rating = array_search($rating, $letters); } // error! types don't match if (gettype($rating) !== gettype($scale)) { $error = 'rating %s and scale %s do not match'; exit(sprintf($error, $_POST['rating'], $_POST['scale'])); } // error! rating is higher than scale if ($rating > $scale) { $error = 'rating %s is greater than scale %s'; exit(sprintf($error, $_POST['rating'], $_POST['scale'])); } // done! print our rating... $stars = round(($rating / $scale) * MAX_STARS, 2); printf('%s stars out of %s (rating: %s scale: %s)', $stars, MAX_STARS, $_POST['rating'], $_POST['scale']); } ?> 

Maybe it's worth explaining what the hell is going on with regular expressions and callbacks;)

For example, take the following regular expression:

 /^(?<char>A\+?)$|^(?<digit>100|10|6|5|4)$/' 

This regular expression defines two named subpatterns. One, called <char> , captures A and A+ ; another called <digit> captures 100 , 10 , 6 , etc.

preg_match() returns 0 if there is no match (or false on error), so we can return false in this case, since this means that the user input (or scale) of POST ed was not valid.

Otherwise, the $match array will contain any committed values ​​using char and (optionally) digit as keys. If the digit key exists, we know that the match is a digit, and we can pass it to a float and return it. Otherwise, we had to map to char , so we can strtoupper() this value and return it:

 return isset($matches['digit']) ? (float) $matches['digit'] : strtoupper($matches['char']); 

Both callbacks are identical (except for the regular expressions themselves), so you can create the called code and possibly save some duplication.

Hopefully at this point it won't start to seem a bit confusing! Hope this helps :)

+6
source

I have a complete opposite point of view. in PHP you can add variables dynamically .

My side of PHP (I'm very lazy):

 <?php const REGEX_INPUT = '/^(?<char>[a-fA-F]{1}[-+]?)$|^(?<digit>[1-9]?[0-9](\.\d+)?|100)$/'; if (preg_match (REGEX_INPUT , $_POST['rating'] ) && preg_match (REGEX_INPUT , $_POST['scale'] )){//filter that input through given regex $f- = 0; $f = 1; //add all of the non numeric possibilities you allow (lowercase) $a = 16; $a+ = 17; $rating = strtolower($_POST['rating']); $scale = strtolower($_POST['scale']) ; try{ $yourstars = 5.0 * $$rating/$$scale; //here is the magic } catch (Exception $e) { }else{ //not allowed inputs } 

You will receive a warning about the numbers, suppress them or identify each allowed possibility, for example, using letters.

Explenation

If the message is F, it is saved as f in the rating of $. now, if I call $ {$ rating}, it calls $ {f}.

In numerical messages the same thing: if the message is 10, I finally call at $ 10. $ 10 is undefined, so php issues a warning and says that it assumes $ 10 = 10.

The versions of others are good too, this is just another aspect for the lazy you.

+2
source

Here's how it works for a numeric value.

How to do a literal evaluation: just do an array while holding AF and assign them numeric value's . Then assign the new variable maximum numerical value (so if A + is 100, then the maximum will be 100) and repeat the same math to calculate the result of 5.

 <?php // Assign your $_POST / $_GET data to these variables $maximum = 100; $rating = 50; // Checks wether the entries are a number, if true then automatically calculates score out of 5 if (ctype_digit($maximum) && ctype_digit($rating)) { $score = ($rating/$maximum)*5; } echo $score; ?> 

I am not familiar with AF counting AF and I can not find any conversion. Perhaps someone with this knowledge may complete the answer for op.

0
source

@ Darrgh The answer almost solves your problem, but it is too important in this. So I make it simple enough and still cover your needs.

Here is my code:

 const MAX_STARS = 5; $letters = [ 'F-', 'F', 'F+', 'G-', 'G', 'G+', 'D-', 'D', 'D+', 'C-', 'C', 'C+', 'B-', 'B', 'B+', 'A-', 'A', 'A+', ]; $scales = [ 4 => 4, 5 => 5, 6 => 6, 10 => 10, 100 => 100, 'A' => 16, 'A+' => 17, ]; $rating = is_numeric($_POST['rating']) ? (float) $_POST['rating'] : strtoupper($_POST['rating']); $scale = is_numeric($_POST['scale']) ? (float) $_POST['scale'] : $_POST['scale']; if (false === $rating) { echo 'please supply a valid rating'; } elseif (!(gettype($rating) === gettype($scale))) { echo 'rating does not match scale'; } else { $scale = $scales[$scale]; if (!is_float($rating)) { $haystack = 'A+' === $scale ? $letters : array_slice($letters, 0, -1); $rating = array_search($rating, $haystack, true); } $stars = round(($rating / $scale) * MAX_STARS, 2); printf('rating: %s. scale: %s. stars: %s', $_POST['rating'], $_POST['scale'], $stars); } 

Maybe this will help you a bit .: D

0
source

Am I really not getting this, or is my mathematical approach not a simple solution? Just copy and paste below into your PHP document and it works? Or is it easy? It’s a problem, because I just combine all the messages and get vars into 1 array as strings, and then just replace the letters or their corresponding numerical value. Then I again process all the values ​​as numbers to go to the same 100-point scale. I have a line containing the rating as a percentage of 100 percent, where 100 is the highest. A star would then be light, like rewarding 0.05 stars for 1% of an additional course (100 * 0.05 stars is 5 starts).

  <form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>"> <label>rating</label> <input name="rating" id="rating" type="text" autofocus> <label>out of</label> <select name="scale"> <option value="100">100</option> <option value="10">10</option> <option value="6">6</option> <option value="5">5</option> <option value="4">4</option> <option value="A">A</option> <option value="A+">A+</option> </select> <input type="submit"> </form> <?php if(!empty($_SERVER['REQUEST_METHOD'])) { // Patterns $patterns = array(); $patterns[0] = '/A$/'; $patterns[1] = '/A\+/'; // Replace with $replacements = array(); $replacements[0] = '80'; $replacements[1] = '100'; // Merge $_GET & $_POST foreach (array_merge($_GET, $_POST) AS $name => $value) { // If string matches pattern then replace A or A+ with value if (is_string($value) && !empty($value) && !is_numeric($value)) { $value = preg_replace($patterns, $replacements, $value); } // All others are either valid or should be multiplied by 10 $value = ($value < 10 ? ($value*10) : $value); }?> <!-- write the value of $_POST['rate'] to text input if post is set --> <script> document.getElementById("rating").value = "<?php echo $value; ?>"; </script> <?php } ?> 
0
source

All Articles