在一组正分数上绘制排名

我有一个积极的分数列表:

[98.5, 85, 50, 50, 23, 0, 0, 0] 

我想为这些分数分配排名:

 [1, 2, 3, 3, 4, 5, 5, 5] 

当两个连续分数具有相同的值时,它们获得相同的等级。 知道如何以function的方式解决这个问题吗?

在Haskell和Ruby中发布,因为我认为这两种解决方案都是可行的并且可以移植

在Ruby中:

 a = [98.5, 85, 50, 50, 23, 0, 0, 0] sorted = a.sort.uniq.reverse a.map{|e| sorted.index(e) + 1} # => [1, 2, 3, 3, 4, 5, 5, 5] 

哈斯克尔:

 {-# LANGUAGE TupleSections #-} import Data.List import Data.Maybe (mapMaybe) import Data.Function (on) rank, rank' :: [Double] -> [Int] rank ls = mapMaybe (fmap (+1) . (`elemIndex` sorted)) ls where sorted = reverse . nub $ sort ls -- Or Fixnum's faster solution rank' = map fst . sortBy (compare `on` snd) . concat . zipWith (\n -> map (n,)) [1..] . groupBy ((==) `on` snd) . sortBy (flip compare `on` snd) . zip [1::Int ..] 

有趣的是, rank几乎是sawa的答案。 nub <=> uniqelemIndex <=> index

与@sawa有点不同:

 a = [98.5, 85, 50, 50, 23, 0, 0, 0] a.sort! {|e1,e2| -e1<=>-e2} # Just in case not already sorted h = Hash[a.uniq.each_with_index.to_a] a.map {|e| h[e]+1} # => [1, 2, 3, 3, 4, 5, 5, 5] 

编辑。 我最初建议:

 h = Hash[a.uniq.zip(1..a.size)] a.map {|e| h[e]} 

如果它们已经排序,则递归

 eqNext (x:xs@(y:_)) acc = let acc1 = if x == y then acc else acc + 1 in acc1 : eqNext xs acc1 eqNext _ _ = [] els = 1 : eqNext ls 1 

让我们检查:

 > let ls = [98.5, 85, 50, 50, 23, 0, 0, 0] > els [1,2,3,3,4,5,5,5] 

相同的非递归

 els = snd $ mapAccumL accF (0,0) ls where accF(ac,prev) b = let a1 = if ac == 0 || b /= prev then ac + 1 else ac in ((a1,b), a1) 

我们是否假设分数已经分类?

以下是如何解决它,如果它们不能被假设排序:

 Prelude Data.List Data.Function> let f = (const .) . (,) :: a -> b -> c -> (a,b) Prelude Data.List Data.Function> let sor s = sortBy ((flip compare) `on` snd) s Prelude Data.List Data.Function> let rank scores = map fst . sor . concat . zipWith (map . uncurry . f) [1..] . groupBy ((==) `on` snd) . sor . zip [1,0..] $ scores Prelude Data.List Data.Function> rank [11,13,13,12] [3,1,1,2] 

首先,为每个分数附加一个反向索引。 然后通过元组的第二个投影按降序排序。 通过第二次投影分组为我们提供了必须具有相同等级的分数列表。 使用等级列表进行压缩将等级1分配给最大数字。 通过使用函数f我们同时丢弃实际分数,使元组成为一对(秩,反向索引)。 现在需要按反向索引的降序对这些元组进行排序,并得到第一个投影 – 排名。

另一个Ruby回答:)

 data = [98.5, 85, 50, 50, 23, 0, 0, 0, 85] data.map{|value| data.select{|item| item > value }.size + 1 } 

=> [1, 2, 4, 4, 6, 7, 7, 7, 2]