9
\$\begingroup\$

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.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ I 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] \$\endgroup\$
    – Cruncher
    Commented Aug 1, 2014 at 19:56
  • \$\begingroup\$ That's a good point, @Cruncher; feel free to start a new thread. \$\endgroup\$
    – Jordan
    Commented Aug 2, 2014 at 22:20

16 Answers 16

8
\$\begingroup\$

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
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Care to comment what this does? \$\endgroup\$
    – corsiKa
    Commented Aug 1, 2014 at 19:20
  • 1
    \$\begingroup\$ 1+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. \$\endgroup\$ Commented Aug 2, 2014 at 5:31
8
\$\begingroup\$

T-SQL (40)

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

Assume @ is a table containing the scores as rows.

\$\endgroup\$
3
\$\begingroup\$

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]
\$\endgroup\$
3
\$\begingroup\$

Python (33 characters)

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

Functionally the same as my J answer.

\$\endgroup\$
1
  • \$\begingroup\$ 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. \$\endgroup\$
    – isaacg
    Commented Aug 1, 2014 at 19:16
3
\$\begingroup\$

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
\$\endgroup\$
2
\$\begingroup\$

STATA (16)

egen b=rank(c),f

Result is in b.

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

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Stata in code golf? This one really opens a can of worms. \$\endgroup\$ Commented Aug 2, 2014 at 4:39
2
\$\begingroup\$

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]
\$\endgroup\$
4
  • \$\begingroup\$ my solution was r l=concat$tail$scanl(\s->map$const$length s+s!!0)[0]$group l with 61 characters \$\endgroup\$ Commented Aug 2, 2014 at 9:32
  • \$\begingroup\$ also, your first solution doesn't work because of monomorphism restriction \$\endgroup\$ Commented Aug 2, 2014 at 9:32
  • \$\begingroup\$ That darn momoprophism restriction... I got it to work in GHCI, but since it won't compile I guess I should remove it... \$\endgroup\$ Commented Aug 2, 2014 at 14:44
  • \$\begingroup\$ You could remake it so it would work \$\endgroup\$ Commented Aug 2, 2014 at 15:37
1
\$\begingroup\$

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.)

\$\endgroup\$
0
1
\$\begingroup\$

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]

\$\endgroup\$
1
  • \$\begingroup\$ You can drop the F= for -2 bytes. Also save another 2 with s=>s.map((n,i)=>p-(p=n)?r=i+1:r,p=-1) \$\endgroup\$ Commented Apr 28, 2022 at 0:22
1
\$\begingroup\$

R, 15

with input stored as vector x,

rank(-x,T,"mi")
\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$
1
\$\begingroup\$

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
\$\endgroup\$
1
\$\begingroup\$

><> (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
\$\endgroup\$
1
  • 1
    \$\begingroup\$ "testing the water" in a fish submission made me smile \$\endgroup\$
    – Ingo Bürk
    Commented Aug 2, 2014 at 23:55
1
\$\begingroup\$

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)
\$\endgroup\$
0
\$\begingroup\$

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);
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.