-1

I am storing a score for each of my users. Each score should be mapped to a rank. E.g., someone with a score of 17 would be ranked blogger because blogger has a score requirement of 15.

$score = 17;

$rank = array(
    15 => array('profile_rank_name' => 'Blogger', 'profile_rank_image' => 'blogger.png'),
    18 => array('profile_rank_name' => 'News Editor', 'profile_rank_image' => 'news_editor.png'),
    23 => array('profile_rank_name' => 'Researcher', 'profile_rank_image' => 'researcher.png'),
    29 => array('profile_rank_name' => 'Publications Assistant', 'profile_rank_image' => 'publications_assistant.png'),
    36 => array('profile_rank_name' => 'Editorial Assistant', 'profile_rank_image' => 'editorial_assistant.png'),
    45 => array('profile_rank_name' => 'Copy Editor', 'profile_rank_image' => 'copy_editor.png'),
)

Because, in this instance, score is 17 then $rank[15] should be returned. Because $score is greater or equal than 15. How could I go about this?

Edit:

Uksort sorts an array by keys using a user-defined comparison function. I'm not sure how it's working internally. In the below function what is $a and what is $b?

if( ! function_exists('cmp'))
{
    function cmp($a, $b)
    {
        return $a;
    }
}

uksort($rank, "cmp");

Edit: I notice the ambiguity in my question, I apologies as iut's 3am and I'm not thinking as clearly as I would normally. Thank you all for replies. I must think of rephrasing the question.

Accepted Answer

public function get_profile_rank($score)
{
    /* This method exists as an optimisation effort. Ranks are defined within the database table `author_profile_rank`.
     * When we don't need application functionality on ranks and we only need to display the rank name and image we
     * call this method. It saves using a table join to retrieve the rank name and image.
     * http://stackoverflow.com/questions/19886351/returning-an-array-key-based-on-a-integer/19886467?noredirect=1#comment29583797_19886467
     */

    if($score <= 17)
    {
        return array('profile_rank_name' => 'Blogger', 'profile_rank_image' => 'blogger.png');
    }
    elseif($score >= 45)
    {
        return array('profile_rank_name' => 'Copy Editor', 'profile_rank_image' => 'copy_editor.png');
    }

    $ranks = array(
        23 => array('profile_rank_name' => 'Researcher', 'profile_rank_image' => 'researcher.png'),
        29 => array('profile_rank_name' => 'Publications Assistant', 'profile_rank_image' => 'publications_assistant.png'),
        36 => array('profile_rank_name' => 'Editorial Assistant', 'profile_rank_image' => 'editorial_assistant.png'),
    );

    $lower = function($val) use ($score)
    {
        if($val <= $score) return TRUE;
    };

    return $ranks[max(array_filter(array_keys($ranks), $lower))];
}
Michael Rich
  • 191
  • 1
  • 11

6 Answers6

2

Loop through your array and set a variable to the item in the array if the value is less than or equal to their score. Break when the value is greater.

I haven't written php in over 10 years, but something like:

foreach($rank as $currentRank=>$rankData){
    if($currentRank <= $score) $matchedRank = $rankData;
    else break;
}
Zephryl
  • 343
  • 2
  • 9
  • 1
    don't work if array is not sorted. If you have two keys `15 et 16` for instance, the `15` before the `16`, your code will return the `15` and break the loop. Instead of returning the good value `16`. – S.Thiongane Nov 10 '13 at 05:13
  • I am assuming it's sorted based on how the question was phrased. I'm not sure your example makes sense. A 15 before a 16 is sorted and would return the 16 if their score is 16 or higher. – Zephryl Nov 10 '13 at 05:44
  • What to expect if `$score = 14`? – Paulo Freitas Nov 10 '13 at 05:51
  • Then $matchedRank will be null. I don't believe that was part of the question. Do you know what the author wants to do when the score is 14? – Zephryl Nov 10 '13 at 06:00
  • It's not in the question as well as the key sorting issue, but to be trouble-proof we've to make all the assumptions... :P – Paulo Freitas Nov 10 '13 at 06:05
2

This does exactly what you want ;)

$score = 17;

$rank = array(
    15 => array('profile_rank_name' => 'Blogger', 'profile_rank_image' => 'blogger.png'),
    18 => array('profile_rank_name' => 'News Editor', 'profile_rank_image' => 'news_editor.png'),
    23 => array('profile_rank_name' => 'Researcher', 'profile_rank_image' => 'researcher.png'),
    29 => array('profile_rank_name' => 'Publications Assistant', 'profile_rank_image' => 'publications_assistant.png'),
    36 => array('profile_rank_name' => 'Editorial Assistant', 'profile_rank_image' => 'editorial_assistant.png'),
    45 => array('profile_rank_name' => 'Copy Editor', 'profile_rank_image' => 'copy_editor.png'),
);

$keys = array_keys($rank);

$lower = function ($val) use ($score){
    if($val <= $score) return true;
};


 $key_res = array_filter($keys, $lower);

 $user_rank = $rank[max($key_res)];

 var_dump($user_rank);

OUTPUT

array (size=2)
  'profile_rank_name' => string 'Blogger' (length=7)
  'profile_rank_image' => string 'blogger.png' (length=11)
S.Thiongane
  • 6,883
  • 3
  • 37
  • 52
1

Might be the answer? based to your question:

$score = 17;

$ranks = array(
    15 => array('profile_rank_name' => 'Blogger', 'profile_rank_image' => 'blogger.png'),
    18 => array('profile_rank_name' => 'News Editor', 'profile_rank_image' => 'news_editor.png'),
    23 => array('profile_rank_name' => 'Researcher', 'profile_rank_image' => 'researcher.png'),
    29 => array('profile_rank_name' => 'Publications Assistant', 'profile_rank_image' => 'publications_assistant.png'),
    36 => array('profile_rank_name' => 'Editorial Assistant', 'profile_rank_image' => 'editorial_assistant.png'),
    45 => array('profile_rank_name' => 'Copy Editor', 'profile_rank_image' => 'copy_editor.png')
);

foreach($ranks as $rank)
{
    if($score >= $rank )
    {
        echo $rank['profile_rank_name']."-".$rank['profile_rank_image'];
    }
}
NosiaD
  • 587
  • 1
  • 6
  • 18
  • Thankyou diason this is how I first approached the problem. I have the ranks in the array stored in a database table and was fetching the rank by doing a join and comparison. This is now an effort to rewrite the SQL functionality in PHP to eliminate the JOIN for better performance. – Michael Rich Nov 10 '13 at 04:56
  • no problemo, might be your first time coding such simple programs :D – NosiaD Nov 10 '13 at 05:00
1

1) sort your array so that ranks are in increasing order (if not sorted the next if will fail)

2) you need to loop your keys not array contents so get keys firsts

$keys = array_keys($ranks)

3) use foreach or for any of the two to loop the array

foreach($keys as $key){
   if($score >= $key){
       echo $ranks[$key];
   }
}

note: if not sorted you will get wrong results when the above code runs, and if there are like 1000 ranks, an effective way would be using binary search to get a rough position of the $score in $ranks

ManZzup
  • 526
  • 4
  • 12
1

Try something like this

<?php
    $score = 17;

$rank = array(
    15 => array('profile_rank_name' => 'Blogger', 'profile_rank_image' => 'blogger.png'),
    18 => array('profile_rank_name' => 'News Editor', 'profile_rank_image' => 'news_editor.png'),
    23 => array('profile_rank_name' => 'Researcher', 'profile_rank_image' => 'researcher.png'),
    29 => array('profile_rank_name' => 'Publications Assistant', 'profile_rank_image' => 'publications_assistant.png'),
    36 => array('profile_rank_name' => 'Editorial Assistant', 'profile_rank_image' => 'editorial_assistant.png'),
    45 => array('profile_rank_name' => 'Copy Editor', 'profile_rank_image' => 'copy_editor.png'),
);


foreach ($rank as $k=>$v)
{
    if($score<=$rank[$k])
    {
        print_r($v);//Prints the first array element.
        break; 
    }
}

OUTPUT :

Array ( [profile_rank_name] => Blogger [profile_rank_image] => blogger.png )
Shankar Narayana Damodaran
  • 68,075
  • 43
  • 96
  • 126
0
$keys = array_keys($rank);
sort($keys); // not needed in your example, just to be *bug* free
$score = 17;
$index = array_reduce($keys, function ($prev, $next) use ($score) {
    return ($prev <= $score && $score < $next) ? $prev : $next;
}) ?: min($keys);
var_dump($index); // int(15)
print $rank[$index]['profile_rank_name']; // Blogger

$index will be the minimum array key if $score is lower than the minimum key. So it works for both $score < min($keys) and $score > max($keys). :)

Working demo: https://eval.in/65190

Demo for $score in range(0, 100): https://eval.in/65196

PS: PHP 5.3+ (I'm using short ternary and closure callback)

Paulo Freitas
  • 13,194
  • 14
  • 74
  • 96
  • Maybe this is SOP for php, but my guess is that it's too fancy. :-) BTW, the solution to this kind of problem is beautiful in C#. array.Where(item => item.Key <= score).Max(item => item.Key) – Zephryl Nov 10 '13 at 05:56
  • 1
    It would be an one-liner if the `sort()` function didn't work by reference. ;/ I agree that it could be too fancy, but it's less fancy than the accepted answer, anyway... – Paulo Freitas Nov 10 '13 at 06:08
  • 1
    PS: downvoter should said I was using wrong variable name inside the closure instead of just downvoting, since it's a complete answer despite the problem already fixed. – Paulo Freitas Nov 10 '13 at 06:14
  • I upvoted you Paulo, credit is deserved for this code, thank you for posting – Michael Rich Nov 10 '13 at 06:45
  • @MichaelRich Thank you! After your upvote someone downvoted my answer for no reason, but whatever, nevermind... :) – Paulo Freitas Nov 10 '13 at 06:52