1

I have the following array:

$learners=array('Eliza'=87,  'Joe'=81, 'Anne'=69, 'Marley'=39, 'Teddy'=39, 'Jemma'=90, 'Sylvia'=87);

So far I have been able to separate the two arrays as follows:

$tudents=array_keys($learners);
$scores=array_values($learners);

The ranking is as follows:

Student    Score    Position
Jemma       90          1
Sylvia      87          2
Eliza       87          2
Joe         81          4
Anne        69          5
Marley      39          7
Teddy       69          7

I would like to create a new array with names as keys and positions as values i.e

$positions=array('Jemma'=1, 'Sylvia'=2, 'Eliza'=2, 'Joe'=4, 'Anne'=5, 'Marley'=7, 'Teddy'=7);

This will allow me to echo any name and position at any point on the script. I am not sure how to proceed.

The ranking is not straightforward if the scores have duplicates. If there is a tie at number 2, the 3rd position is skipped. If the tie occurs at the end of the scores, then both scores will be placed at the last position and the preceding position will be skipped, in the example above, position 6 has been skipped and the two 39s occupy position 7.

Any help will be appreciated

2
  • Can we assume you have the ranks already or is your question asking how do you compute those ranks? Commented Nov 1, 2013 at 22:54
  • What about array_merge()? php.net/manual/en/function.array-merge.php Commented Nov 1, 2013 at 23:23

3 Answers 3

1
// Sort decending
arsort($data);

$vals  = array_values($data);
$last  = end($vals);   // The lowest score
$prev  = null;
$rank  = 0;

$positions = array();
foreach($data as $student => $score) {
    if ($score == $last) {
        // The score is the same as the lowest, the rank is set to last position
        $rank = count($data);
    } else if ($prev != $score) {
        // We only update if the score is not the same as prev
        $rank++;
    }  else if ($prev == $score) {
     // We matched on the key, replace any items with the
     // same score with the current rank 
     $matches = array_keys($positions, $score);
     foreach($matches as $key) {
       $positions[$key] = $rank;
     }
     $positions[$student] = $rank;
     // Now skip ahead to the next rank +1
     $rank = $rank + count($matches) + 1;
     continue;
    }
    $positions[$student] = $rank;
    $prev = $score; // Remember the previous score
}
var_dump($positions);
Sign up to request clarification or add additional context in comments.

4 Comments

thanks, your answer works to some extent. The problem is that it does not skip a position or positions when the tie occurs. For instance there is a tie at number 2, so the next student should be assigned position 4, if 3 students tied, then the next would have been position 5 and so on. Neverthe lsess, it is offering me some insight into how this can e approached. Is it posibble to remove the int from output?
@Bululu i've updated, although the change is not tested i think it should work
Wow, I have tested it, it works very well regardless of ties. Thaks a lot.
I have two equally GREAT answers. Thanks go Bill Karwin's and AlexP answer. Bill Karwin's answer also solves the problem just as AlexP's answer. I have marked AlexP's answer as the accepted answer simply because @Bill Karwin has a much higher Reputation than AlexP so I figured AlexP needs an accepted answer more than Bill Karwin does to boost his points! Just hope that is fair.
1

Here's another solution:

First sort by value (the print_r is just to check progress).

arsort($learners);
print_r($learners);

Then make an array of rankings, but don't advance the rank if the score is the same as the previous element's score.

$rank = $pos = 1;
$prev_score = current($learners);
foreach ($learners as $name => $score) {
    if ($score != $prev_score) {
        $rank = $pos;
    }
    $ranking[$name] = $rank;
    $prev_score = $score;
    $pos++;
}
print_r($ranking);

Now correct the last entries, any element with the same score as the last element should be in 7th place. There's a rarely-used argument to array_keys() that searches for a given value.

$low_score = end($learners);
$last_place = count($learners);
foreach (array_keys($learners, $low_score) as $name) {
    $ranking[$name] = $last_place;
}
print_r($ranking);

Output:

Array
(
    [Jemma] => 90
    [Sylvia] => 87
    [Eliza] => 87
    [Joe] => 81
    [Anne] => 69
    [Marley] => 39
    [Teddy] => 39
)
Array
(
    [Jemma] => 1
    [Sylvia] => 2
    [Eliza] => 2
    [Joe] => 4
    [Anne] => 5
    [Marley] => 6
    [Teddy] => 6
)
Array
(
    [Jemma] => 1
    [Sylvia] => 2
    [Eliza] => 2
    [Joe] => 4
    [Anne] => 5
    [Marley] => 7
    [Teddy] => 7
)

1 Comment

this is useful, nice array manipulation! Is it possible to have to combine the two and have one olution that corrects both issues? Like one that takes into consideration a tie at the end and a tie elsewhere n the array? I am trying tyo see how I can modify it.
0

Looks like PHP, right?

Basically go through your initial list and stuff them into a new array that uses the names as keys (you're in trouble here if two people have the same name, but I'm assuming this is a homework assignment and that's not an issue?)

$sorted = array();
for ($i=0;$i<count($positions);$i++) {
 if (!isset($sorted[$positions[$i]["Student"]])) {
  $sorted[$positions[$i]["Student"]] = $positions[$i]["Score"];
 } else if ($sorted[$positions[$i]["Student"]]<$positions[$i]["Score"] {
  $sorted[$positions[$i]["Student"]] = $positions[$i]["Score"];
 }
}

What you're doing here is making an array where the KEY is the name of the student, and putting the first score you find in as the VALUE for that. So $sorted["Jemma"] = 90. Then if you hit that name again, and the score is higher than the current value for $sorted["Jemma"], you're replacing it.

After that, you run an arsort($sorted) to put it in order.

1 Comment

Also there's plenty that can be optimized in the above code but I'm trying to make it explicit what you want to do.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.