Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I rank array values with duplicate values and skipping some positions if there is a tie?

Tags:

php

mysql

I am working on a Database that manipulates college students exam results. Basically, I am pulling the records from a MySq Database, puling one class at any given time. I want to rank rank the students, with the highest perfomer at number 1.
Here is an illustration;

Marks: 37, 92, 84, 83, 84, 65, 41, 38, 38, 84.  

So I want to capture mysql data as a single array. Once I have the data in an array, I should then assign each student a position in the class such as 1/10 (number 1, the 92 score), 4/10 etc. Now the problem is that if there is a tie, then the next score skips a position and if there are 3 scores at one position then the next score skips 2 positions. So the scores above would be ranked as follows;

92 - 1
84 - 2,
84 - 2,
84 - 2,
83 - 5,
65 - 6,
41 - 7,
38 - 8,
38 - 8 ,
37 - 10

The grading system requires that the number of positions (ranks, if you will) will be maintained, so we ended up with 10 positions in this class since positions 3, 4, 5 and 9 did not have any occupants. (The alternative of filling every number will have given us only 8 positions!)

Is it possible (humanly possible /php possible) to use PHP to rank the scores above in such a way that it can handle possible ties such as 4 scores at one position? SADLY, I could not come up with a function to do this. I need a PHP function (or something ... PHP) that will take an array and produce a ranking as above.

Any help will be deeply appreciated, though I think I may be asking for too much. If it's possible to do this with MySQL query data without having it in an array, then that will also be helpful!

like image 202
Bululu Avatar asked May 28 '11 17:05

Bululu


2 Answers

I assume the grades are already sorted by the database, otherwise use sort($grades);.

Code:

$grades = array(92, 84, 84, 84, 83, 65, 41, 38, 38, 37);
$occurrences = array_count_values($grades);
$grades = array_unique($grades);
foreach($grades as $grade) {
    echo str_repeat($grade .' - '.($i+1).'<br>',$occurrences[$grade]);
    $i += $occurrences[$grade];
}

Result:

92 - 1
84 - 2
84 - 2
84 - 2
83 - 5
65 - 6
41 - 7
38 - 8
38 - 8
37 - 10

EDIT (Response to discussion below)

Apparently, in case the tie occurs at the lowest score,
the rank of all lowest scores should be equal to the total count of scores.

Code:

$grades = array(92, 84, 84, 84, 83, 65, 41, 38, 37, 37);
$occurrences = array_count_values($grades);
$grades = array_unique($grades);
foreach($grades as $grade) {
    if($grade == end($grades))$i += $occurrences[$grade]-1;
    echo str_repeat($grade .' - '.($i+1).'<br>',$occurrences[$grade]);
    $i += $occurrences[$grade];
}

Result:

92 - 1
84 - 2
84 - 2
84 - 2
83 - 5
65 - 6
41 - 7
38 - 8
37 - 10
37 - 10
like image 107
Anne Avatar answered Oct 19 '22 23:10

Anne


$scores = array(92, 84, 84, 84, 83, 65, 41, 38, 38, 37);
$ranks = array(1);
for ($i = 1; $i < count($scores); $i++)
{
    if ($scores[$i] != $scores[$i-1])
        $ranks[$i] = $i + 1;
    else
        $ranks[$i] = $ranks[$i-1];
}
print_r($ranks);
like image 29
Yuri Stuken Avatar answered Oct 19 '22 23:10

Yuri Stuken