Get dense rank and gapped rank for all items in array
Asked Answered
R

2

2

I want to calculate and store the dense rank and gapped rank for all entries in an array using PHP.

I want to do this in PHP (not MySQL because I am dealing with dynamic combinations 100,000 to 900 combinations per week, that’s why I cannot use MySQL to make that many tables.

My code to find the dense ranks is working, but the gapped ranks are not correct.

PHP code

$members = [
    ['num' => 2, 'rank' => 0, 'dense_rank' => 0],
    ['num' => 2, 'rank' => 0, 'dense_rank' => 0],
    ['num' => 3, 'rank' => 0, 'dense_rank' => 0],
    ['num' => 3, 'rank' => 0, 'dense_rank' => 0],
    ['num' => 3, 'rank' => 0, 'dense_rank' => 0],
    ['num' => 3, 'rank' => 0, 'dense_rank' => 0],
    ['num' => 3, 'rank' => 0, 'dense_rank' => 0],
    ['num' => 5, 'rank' => 0, 'dense_rank' => 0],
    ['num' => 9, 'rank' => 0, 'dense_rank' => 0],
    ['num' => 9, 'rank' => 0, 'dense_rank' => 0],
    ['num' => 9, 'rank' => 0, 'dense_rank' => 0]    
];

$rank=0;
$previous_rank=0;
$dense_rank=0;
$previous_dense_rank=0;
foreach($members as &$var){
    //star of rank
    if($var['num']==$previous_rank){
        $var['rank']=$rank;
    }else{
        $var['rank']=++$rank;
        $previous_rank=$var['num'];   
    }//end of rank
    
    //star of rank_dense
    if($var['num']===$previous_dense_rank){
        $var['dense_rank']=$dense_rank;
        ++$dense_rank;
    }else{
        $var['dense_rank']=++$dense_rank;
        $previous_dense_rank=$var['num'];
    }   
    //end of rank_dense
    
    echo $var['num'].' - '.$var['rank'].' - '.$var['dense_rank'].'<br>';
}
?>

My flawed output is:

num rank dynamic rank
2 1 1
2 1 1
3 2 3
3 2 3
3 2 4
3 2 5
3 2 6
5 3 8
9 4 9
9 4 9
9 4 10

Notice when the error happens and there is a higher number in the number column it corrects the error in that row. See that when the number goes from 3 to 5.

Ratty answered 28/6, 2022 at 18:6 Comment(0)
K
1

Given that your results are already sorted in an ascending fashion...

  • For dense ranking, you need to only increment your counter when a new score is encountered.

  • For gapped ranking, you need to unconditionally increment your counter and use the counter value for all members with the same score.

??= is the "null coalescing assignment" operator (a breed of "combined operator"). It only allows the right side operand to be executed/used if the left side operand is not declared or is null. This is a technique of performing conditional assignments without needing to write a classic if condition.

Code: (Demo)

$denseRank = 0;
$gappedRank = 0;
foreach ($members as &$row) {
    $denseRanks[$row['num']] ??= ++$denseRank;
    $row['dense_rank'] = $denseRanks[$row['num']];
    
    ++$gappedRank;
    $gappedRanks[$row['num']] ??= $gappedRank;
    $row['rank'] = $gappedRanks[$row['num']];
    
    // for better presentation:
    echo json_encode($row) . "\n";
}

Output:

{"num":2,"rank":1,"dense_rank":1}
{"num":2,"rank":1,"dense_rank":1}
{"num":3,"rank":3,"dense_rank":2}
{"num":3,"rank":3,"dense_rank":2}
{"num":3,"rank":3,"dense_rank":2}
{"num":3,"rank":3,"dense_rank":2}
{"num":3,"rank":3,"dense_rank":2}
{"num":5,"rank":8,"dense_rank":3}
{"num":9,"rank":9,"dense_rank":4}
{"num":9,"rank":9,"dense_rank":4}
{"num":9,"rank":9,"dense_rank":4}

For the record, if you are dealing with huge volumes of data, I would be using SQL instead of PHP for this task.

Klagenfurt answered 28/6, 2022 at 22:57 Comment(0)
B
0

It seems like you want the dynamic rank to be sequential?

Your sample data appears to be sorted, if this remains true for your real data then you can remove the conditional and just increment the variable as you assign it:

//start of rank_dense
$var['dense_rank']=++$dense_rank;
//end of rank_dense

It sounds like you're saying you won't be implementing a database. Databases like MySQL can easily handle the workload numbers you outlined and they can sort your data as well. You may want to reconsider.

Buster answered 28/6, 2022 at 18:45 Comment(1)
Thanks for your help. I'm a newbie here. I believe I need the 2 if statements. To enter previous dance rank number are the new dance rank number into the array. I am using MySQL the array is sample table. Every week there be one MySQL table. The users can enter appropriate numbers see where they be in the pool. If I used MySQL, I be entering every week 100,00 combinations to 1000. I do not want to enter that many combinations per week. @K.BRatty

© 2022 - 2024 — McMap. All rights reserved.