1

What I'm trying to do is creating a custom rating system like this:

AAA - Highest rating
AA
A
BBB
BB
B
CCC
CC
C - Lowest rating

In which AAA is the highest and C the lowest. For this this to work I need PHP to know which rating is the highest, lowest and everything in between and evaluate series of ratings based on that. I allready figured out how to create a sorting with usort() like so:

$ratings = array("a" => "AAA", "b" => "AA", "c" => "A", "d" => "BBB", "e" => "BB", "f" => "B", "g" => "CCC", "h" => "CC", "i" => "C");
$sortedRatings = usort($ratings, "cmp_function");

This will return an array neatly sorted from highest priority to lowest. Now I need to go ahead and use this sorting to get the highest and lowest rating from an array like this one:

$ratingHistory = array("BB", "B", "CCC", "C", "BB");

So how could I go about getting the highest and lowest value from $ratingHistory based on the sorting as in $sortedRatings ? I hope someone can help me out with this one. If my problem isn't completely clear to you, drop a comment so I can try to explain further.

Edit:

Just to be clear. The expected outcomes would be:

Highest: BB
Lowest: C
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Frank Kluytmans
  • 533
  • 2
  • 10
  • 25
  • If you can sort the array, then Highest value is first key, Lowest is last key. – fusion3k Mar 29 '16 at 14:22
  • How about sorting and selecting the first and last element of the array? That would be effective and simple but not very efficient. – jojonas Mar 29 '16 at 14:23

3 Answers3

2

This solution works with your original $ratings array and — through usort() — sort $ratingHistory:

$ratings = array("a" => "AAA", "b" => "AA", "c" => "A", "d" => "BBB", "e" => "BB", "f" => "B", "g" => "CCC", "h" => "CC", "i" => "C");
$ratingHistory = array("BB", "B", "CCC", "C", "BB");

usort
(
    $ratingHistory,
    function( $a, $b ) use( $ratings )
    {
        return strcmp( array_search( $a, $ratings ), array_search( $b, $ratings ) );
    }
);

$result = array( 'max'=>current($ratingHistory), 'min'=>end($ratingHistory) );

print_r( $result );

Will print:

Array
(
    [max] => BB
    [min] => C
)

Performance:

I have compared the performance of above example with a foreach(), an array_map() (all cases with both associative and enumerated array as $ratings and with different $ratingHistory sizes). The usort() method is in anyhow far the more efficient. This because you have anyway to iterate complete array, but with usort you can use less commands/functions/comparisons.

fusion3k
  • 11,568
  • 4
  • 25
  • 47
1

You can try this code :

$ratings = array("", "AAA","AA", "A", "BBB", "BB", "B", "CCC", "CC", "C");
$ratingHistory = array("BB", "B", "CCC", "C", "BB");

$min = 0;
$max = INF;
foreach ($ratingHistory as $row)
{
    $rate = array_search($row, $ratings);
    if ($rate && $rate > $min) {
        $min = $rate;
    }
    if ($rate && $rate < $max) {
        $max = $rate;
    }
}

echo 'Min : '. $ratings[$min];
echo '<br />Max : '. $ratings[$max];

If you test, the values are BB and C for your example.

Vincent Decaux
  • 9,857
  • 6
  • 56
  • 84
  • This seems to work fine except for it doesn't recognize AAA as the highest rating. I'm working on a solution for that and will post it when found. Thanks for your answer, I will accept it when I'm able to complete it ;) – Frank Kluytmans Mar 29 '16 at 14:56
  • Yes, weird. You can put an empty string in $ratings before the "AAA". It solves this problem. Look to my edit. – Vincent Decaux Mar 29 '16 at 17:43
0

Because ALL of your encounterable values are in your ratings lookup array, you can enjoy the sweet brevity of array_intersect(). This will remove all values from the $ratings array which are not found in the $ratingsHistory array. Of course, the $ratings array is already in order, so the results are instantly accessible from the first and last elements.

Code: (Demo)

$ratingHistory = array_intersect($ratings, $ratingHistory);
var_export([
    'min' => $ratingHistory[key($ratingHistory)],
    'max' => $ratingHistory[array_key_last($ratingHistory)]
]);

If you don't want to maintain a lookup array (to allow your code to be more dynamic/robust and require less maintenance), spell out the logic. Using a linear foreach loop will have a smaller time complexity and will offer better performance than a non-linear sort like usort().

Code: (Demo)

$result = ['min' => null, 'max' => null];
foreach ($array as $value) {
    if (
        !$result['min']
        || (!trim($value, $result['min']) ? $value < $result['min'] : $value > $result['min'])
    ) {
        $result['min'] = $value;
    }
    if (
        !$result['max']
        || (!trim($value, $result['max']) ? $value > $result['max'] : $value < $result['max'])
    ) {
        $result['max'] = $value;
    }
}
var_export($result);

Maintaining a lookup array for priorities and calling usort() is also a clean approach, but this has been asked before on Stack Overflow and it will have the poorest time complexity score of these three snippets. 1 2 3 4

Code: (Demo)

$lookup = array_flip($ratings);
usort($ratingHistory,  fn($a, $b) => $lookup[$a] <=> $lookup[$b]);
var_export([
    'min' => $ratingHistory[0],
    'max' => $ratingHistory[array_key_last($ratingHistory)]
]);

P.s. Any technique that is making iterated calls of array_search() will have worse performance than ALL of my snippets above. "Value searching" an array is always going to be slower than "key searching" an array in PHP.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136