Rank a list of scores with "skips"

8

1

Given a list of scores (non-negative integers) pre-sorted from greatest to least:

[ 10, 10, 6,  6,  4,  0]

Assign each score an integer rank, beginning with 1 and ascending, such that equal scores have the same rank (i.e. they are tied):

[ 1, 1, 3, 3, 5, 6 ]

In the case of ties, ranks are "skipped," e.g. since the first and second-greatest scores (10 and 10) are tied, they both have rank 1, and rank 2 is "skipped," so the third-greatest score (6) has rank 3.

Output a list of non-descending ranks corresponding to the input scores.

Examples

In:  10 10  6  6  4  0
Out:  1  1  3  3  5  6
In:  10  9  8
Out:  1  2  3
In:   0  0  0
Out:  1  1  1
In:  16 15 15 12 11 11 10  9  9  9  8  2  2  2  0
Out:  1  2  2  4  5  5  7  8  8  8 11 12 12 12 15

Input

Assume all scores will be between 0 and 1,000 inclusive, and the input will have no more than 500 scores. Input can be in whatever format is convenient for your language of choice (including but not limited to STDIN, arguments to a function, an array already stored in a variable, etc.).

Output

Return or store in a variable the resulting ordered list of ranks, or write it to STDOUT in a human-readable way (e.g. 1 2 3, [1,2,3], 1\n2\n3\n, and { 1, 2, 3 } are all fine; 123 is not, for want of a delimiter). The input scores may be stored/printed along with their corresponding output ranks, but that's not required.

Restrictions

You may use any standard library your language offers. Standard loopholes apply.

Winning conditions

This is , so the smallest program (in bytes) wins. In case of a tie, the answer with the most votes wins.

Notes

This is based on a Ruby question on SO that generated some interesting answers, including one very short one. I encourage you to come up with your own solutions before looking there.

Jordan

Posted 2014-08-01T16:25:05.227

Reputation: 5 001

1I think this would be better, and illicit better answers, if it was not presorted, and the ranks needed to retain their original order. That is [10, 4, 6, 0, 6, 10] would be [1, 5, 3, 6, 3, 1] – Cruncher – 2014-08-01T19:56:55.373

That's a good point, @Cruncher; feel free to start a new thread. – Jordan – 2014-08-02T22:20:07.630

Answers

8

J (7 6)

EDIT: Oh, wait! It doesn't need to be a function!

>:i.~y

Thank god for i.~...

>:@:i.~

Or as a named function (3 chars more, but not functionally different):

f=:>:@:i.~

Run tests:

   f=:>:@:i.~
   f 10 10  6  6  4  0
1 1 3 3 5 6
   f 10  9  8
1 2 3
   f 0  0  0
1 1 1
   f 16 15 15 12 11 11 10  9  9  9  8  2  2  2  0
1 2 2 4 5 5 7 8 8 8 11 12 12 12 15

ɐɔıʇǝɥʇuʎs

Posted 2014-08-01T16:25:05.227

Reputation: 4 449

1Care to comment what this does? – corsiKa – 2014-08-01T19:20:32.647

11+i.~ is the kind of train that can be both assigned and used inline, so can be used as a function without its usual train parens. That's 5 chars. And for the record, @ does the same job as @: in this case, so you could've saved an easy character there. – algorithmshark – 2014-08-02T05:31:52.270

8

T-SQL (40)

SELECT RANK()OVER(ORDER BY B DESC)
FROM @

Assume @ is a table containing the scores as rows.

bmarks

Posted 2014-08-01T16:25:05.227

Reputation: 101

3

Python (33 characters)

lambda x:[1+x.index(i)for i in x]

Functionally the same as my J answer.

ɐɔıʇǝɥʇuʎs

Posted 2014-08-01T16:25:05.227

Reputation: 4 449

Because input can be in the format of your choice, you can declare the array to be stored in x to begin with, and "output" it by storing the result in a variable. – isaacg – 2014-08-01T19:16:31.597

3

Pyth, 6

m'XYdY

List is stored in Y to begin with. This is functionally the same as the 22 character ruby solution: map over d in Y to index of d in Y plus 1, then print.

Example:

$ echo "=Y[16 15 15 12 11 11 10 9 9 9 8 2 2 2 0)m'XYdY" | python3 pyth.py

[1, 2, 2, 4, 5, 5, 7, 8, 8, 8, 11, 12, 12, 12, 15]

isaacg

Posted 2014-08-01T16:25:05.227

Reputation: 39 268

3

APL, 2 bytes

⍳⍨

In ⎕IO←1. Dyadic iota searches its right argument into its left argument. The operator copies the right argument to the left argument if the operand is used monadically. Hence the solution simply searches the position of each one of the elements of the vector given in itself.

Samples:

    ⍳⍨10 10 6  6  4  0
1 1 3 3 5 6
    ⍳⍨0  0  0 
1 1 1 
    ⍳⍨16 15 15 12 11 11 10  9  9  9  8  2  2  2  0
1 2 2 4 5 5 7 8 8 8 11 12 12 12 15

lstefano

Posted 2014-08-01T16:25:05.227

Reputation: 850

2

STATA (16)

egen b=rank(c),f

Result is in b.

Assumes c is a variable in the dataset containing the input.

bmarks

Posted 2014-08-01T16:25:05.227

Reputation: 101

1Stata in code golf? This one really opens a can of worms. – shadowtalker – 2014-08-02T04:39:13.093

2

Haskell (31)

f x=succ.(`elemIndexJust`x)<$>x -- Requires the Safe module

Usage:

f [10,10,6,6,4,0] --evaluates to [1,1,3,3,5,6]

recursion.ninja

Posted 2014-08-01T16:25:05.227

Reputation: 548

my solution was r l=concat$tail$scanl(\s->map$const$length s+s!!0)[0]$group l with 61 characters – proud haskeller – 2014-08-02T09:32:04.320

also, your first solution doesn't work because of monomorphism restriction – proud haskeller – 2014-08-02T09:32:31.197

That darn momoprophism restriction... I got it to work in GHCI, but since it won't compile I guess I should remove it... – recursion.ninja – 2014-08-02T14:44:31.707

You could remake it so it would work – proud haskeller – 2014-08-02T15:37:35.467

1

So as to establish a baseline:

Ruby (38)

Assuming a is an array:

r,i=1,0;a.map{|x|i+=1;x==a[i-2]?r:r=i}

(This is based on falsetru's answer on the original SO thread and isn't my original work. I know there's a Ruby solution that's 22 characters, but I'd like to see someone come up with one shorter than that in Ruby.)

Jordan

Posted 2014-08-01T16:25:05.227

Reputation: 5 001

1

JavaScript (E6) 41

A function with an array argument, returning an array

F=s=>s.map((n,i)=>p-n?(p=n,r=i+1):r,p=-1)

Test In Firefox console

F([10,10,6,6,4,0])

Output: [1, 1, 3, 3, 5, 6]

F([16, 15, 15, 12, 11, 11, 10, 9, 9, 9, 8, 2, 2, 2, 0])

Output: [1, 2, 2, 4, 5, 5, 7, 8, 8, 8, 11, 12, 12, 12, 15]

edc65

Posted 2014-08-01T16:25:05.227

Reputation: 31 086

1

R, 15

with input stored as vector x,

rank(-x,T,"mi")

shadowtalker

Posted 2014-08-01T16:25:05.227

Reputation: 461

1

Powershell (70)

$n=1;$c=0;$l=$a[0];$a|%{if($l-eq$_){$n}else{$n=$c+1;$n}$l=$a[$c];$c++}

It's only 51 characters if you take out the variable assignments at the beginning, which makes me feel slightly less inadequate.

Assumes $a is assigned and sorted as specified by the problem. $n tracks rank, $c is just a counter which works with $l, the last element checked in the array.

If there's anything I can do to improve this, I'd love to know.

fuandon

Posted 2014-08-01T16:25:05.227

Reputation: 309

1

Java (57)

Using the same 'rules' as Allbeert:

Constant i is defined as int[] array and contains the input, z contains the size of the input. Others, l,c,x and n, are defined as int.

The left over snippet of code is:

l=0;c=1;for(x=0;x<z;x++){n=i[x];i[x]=n==l?c:(c=x+1);l=n;}

The result is in the input array.

Roy van Rijn

Posted 2014-08-01T16:25:05.227

Reputation: 1 082

1

Ruby, 22

I haven't looked at the SO thread but I imagine this is what they came up with.

a.map{|i|a.index(i)+1}

Edit: Yep, it is. I doubt it's possible to get smaller in Ruby, unless you assume you're defining it as an Array method, then you can do it in 18 characters with

map{|i|index(i)+1)

But of course the full program around that snippet looks like

class Array
  def ranks
    map{|i|index(i)+1)
  end
end

p [1, 2, 2, 4, 5, 5, 7, 8, 8, 8, 11, 12, 12, 12, 15].ranks

histocrat

Posted 2014-08-01T16:25:05.227

Reputation: 20 600

1

><> (47)

Not particularly optimized, just testing the water with my first golf.

r:1:nr2&>ao$:@=?vr~&:|   
&1+&l3)?^;      >r:nr

Assumes that the input is prepopulated in the stack, such that the first element of the input is the first to be popped off.

Testing:

fish.py ranks.fish -v 1 2 3 4 5 6 7 8 9 9 10 10

outputs

1
1
3
3
5
6
7
8
9
10
11
12

Mike Precup

Posted 2014-08-01T16:25:05.227

Reputation: 281

1"testing the water" in a fish submission made me smile – Ingo Bürk – 2014-08-02T23:55:15.773

1

Clojure, 35

With some Java interop mixed in:

(fn[l](map #(+ 1(.indexOf l %)) l))

REPL session:

golf> ((fn[l](map #(+ 1(.indexOf l %)) l)) [10 10  6  6  4  0])
(1 1 3 3 5 6)
golf> ((fn[l](map #(+ 1(.indexOf l %)) l)) [16 15 15 12 11 11 10  9  9  9  8  2  2  2  0])
(1 2 2 4 5 5 7 8 8 8 11 12 12 12 15)

YosemiteMark

Posted 2014-08-01T16:25:05.227

Reputation: 213

0

C - 62

As a code snippet, since there was no requirement for function or full program.

Assumes a, n, j, and k are already defined as int*, int, int, and int respectively, where a is an array containing the input, and n contains the length of the input.

This fails for input of length 0, in which case 3 more characters are needed.

printf("1");for(k=j=1;++j<=n;)printf(" %d",*a-*(a+++1)?k=j:k);

Allbeert

Posted 2014-08-01T16:25:05.227

Reputation: 489