php仿excel的rank函數也是借鑑網上的代碼,可是沒有二維數組狀況下的進行rank排名,因此本身對代碼稍微改了一下,能夠直接運行試驗。php
<?php $arr = array( array('s'=>'99','r'=>'1','a'=>'a'), array('s'=>'99','r'=>'1','b'=>'b'), array('s'=>'100','r'=>'2','c'=>'c'), array('s'=>'101','r'=>'3','d'=>'d') ); echo '<pre>'; print_r(rank($arr,'s','r')); //得到一組數的名次的數組 function rank(array $array,$s,$r){ foreach($array as $k=>$v){ $marr[] = $v[$s]; } foreach($array as $val){ $repeat=get_array_repeats($val[$s],$marr); $num=gt_array_values($val[$s],$marr); $rank[$r]=count($marr)-$num-$repeat+1; $rank2[] = array_merge($val,$rank); } return $rank2; } //得到比本身數小的個數 function gt_array_values($val,array $array){ $num=0; for($i=0;$i<count($array);$i++){ if($val>$array[$i]){ $num++; } } return $num; } //得到這個數的重複次數 function get_array_repeats($string,array $array) { $count = array_count_values($array); foreach ($count as $key => $value) { if ($key == $string) { return $value; } } }
邏輯也很簡單,學生的名次,等於總人數減去比本身成績低的人數,再減去和本身成績同樣的人數,再加一。html
比本身成績低的人數,和與本身成績同樣多的人數,分別是兩個獨立的函數,前者須要循環全部學生成績,比本身成績低的進行人數累加,每一個學生都要進行這個循環,有點耗性能哈。後者只是單純引用數組函數解決問題。
算法
這樣就能夠根據考試成績(字段名s),獲得考生的排名(字段名r),可是很明顯循環次數有點多,不可思議當數據量很大的時候狀況是怎樣的。sql
若是不考慮二維數組的狀況,有大神提出能夠這樣寫:數組
$arr = array(195,180,180,161); function rt($arr){ $temp = $arr; rsort($temp); $temp = array_unique($temp); foreach ($arr as $k1 => $v1) { foreach ($temp as $k2 => $v2) { if($v1==$v2){ $res[$k1] = $k2+1; } } } return $res; }
至於原理,說白了,rsort這個函數直接幫你把排名都排好了,鍵是排名,值是分數,是否是這個道理。而後循環原數組,分數同樣的,把這個排名取出來就好了。ide
至於這種方法,二維數組的時候要怎麼弄,這個須要二維數組裏的分數值先按順序排好,(這是另一種算法),而後套用。函數
固然,這個方法理論上比上一個方法效率好多了。
性能
另外查詢的時候還能夠直接執行sql語句實現rank函數功能,這個網上有很多例子,如MySQL中rank函數如何實現excel
在這個例子中,當咱們要查找id爲1的學員的rank值的時候,直接運行sql語句以下:htm
select tmp.id,tmp.name,tmp.score, @j:=@j+1 as j, @k:=(case when @pre_score=tmp.score then @k else @j end) as rank, @pre_score:=tmp.score as pre_score from ( select * from score where id =1 ) as tmp, (select @k :=0,@j:=0, @pre_score:=0) as mscore
j是順序號,k是rank值
@k:=(case when @pre_score=tmp.score then @k else @j end
這句話意思是隻有在先後二次排序值不一樣時纔會使用順序號,是關鍵的地方。
最後運行sql,用getRow函數獲得id爲1的這條數據,獲得rank值。