Search code examples
phpsortingranking

How can I do a ranking on array values without ties?


I have an array representing ranks or places (like in a game or contest):

rank = [1,3,2,1]

I want the output to be as follows:

rank = [1,4,3,2]

This means, for any tied place, the tie is broken for each OTHER tying place and all other subsequent places are also incremented by one. It's a simple mapping assignment.

Other cases:

rank = [1,1,2,3] -> [1,2,3,4]
rank = [2,1,2,3] -> [2,1,3,4]
rank = [2,1,2,3] -> [2,1,3,4]
rank = [1,1,1,1] -> [1,2,3,4]

Solution

  • Try this:

    <?php
    
    class Ranker 
    
    {
    private $rank, $result, $doWork;
    
    public function rank() {
    
        $rank = array(
            "slot1" => 1,
            "slot2" => 1,
            "slot3" => 1,
            "slot4" => 1
            );
        $result = array();
    
        asort($rank);
    
        /* Get funky */
        foreach ($rank as $place) {
    
            $initialplace = $place;
    
            if (!empty($result)) {
                 {
                    while(in_array($place, $result)) {
                        $place++;
                        if (!in_array($place, $result)) {
                            break;
                        } 
                    }
                }
    
                foreach (array_keys($rank, $initialplace) as $key) {
    
                    if (!array_key_exists($key, $result)) {
                        $result[$key] = $place;
                        break;
                    }
    
                }
            } else {
                /* array_search always returns first match */
                $result[array_search($initialplace, $rank)] = $initialplace;
            }
        }
    
        ksort($result);
    
        /* Printing it out */
        foreach ($result as $finalplace) {
            echo $finalplace . ' ' . array_search($finalplace, $result) . '</br>';
        }
    }
    }
    
    /* Execute class function */
    $ranker = new Ranker;
    $doWork = $ranker->rank();
    
    ?>
    

    OK so this mostly works, the only deviation from the mappings above is that it'll assign the tie breaks for tying scores a little differently but I don't think that should matter really. For example:

        $rank = array(
            "slot1" => 1,
            "slot2" => 1,
            "slot3" => 1,
            "slot4" => 1
            );
    

    will return:

    4 slot1
    3 slot2
    2 slot3
    1 slot4
    

    Instead of [1, 2, 3, 4]. Since they're all tied though and have the same 'initial rank/place/whatever' I don't seem that as being particularly bad - that's to say it's arbitrary how you decide to assign the second guy who got a '1' or the fourth and so on.

    In any event that's a business logic decision and since you didn't share what the use case was I can't really say more about that. However, it seems you have some flexibility in how you actually "break" the ties. So this works in the event that your use case and business logic is flexible. Otherwise, you can modify this slightly to conform more closely to the precise mappings listed above.