Compare rating by array of positive ratings

I have a list of positive points:

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

I would like to assign ranks to these points:

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

If two consecutive points have the same value, they get the same rank. Any idea how to solve this problem functionally?

(published in Haskell and Ruby because I think both solutions will be feasible and can be ported)

+4
ruby haskell
source share
6 answers

In 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] 
+11
source share

Haskell:

 {-# 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 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 ..] 

Interestingly, rank is largely the answer to writing. nub elemIndex uniq and elemIndex index .

+6
source share

Ever-so-is slightly different from @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] 

Ed. First I suggested:

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

If they are already sorted, Fractal

 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 

Check:

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

same non-recursive :

 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) 
+1
source share

Suppose ratings are already sorted or not?

Here's how to solve it if they cannot be considered sorted:

 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] 

First, attach a reverse index to each account. Then sort in descending order by the second projection of the tuple. Grouping by the second projection gives us a list of lists of points, which should have the same rank. Zipping with a list of ranks assigns rank 1 to the largest number. Using the function f , we at the same time discard the actual score by making tuples a pair (rank, reverse index). Now you need to sort these tuples in descending order of the inverse index and get the first projection - rank.

+1
source share

Another Ruby answer :)

 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]

+1
source share

All Articles