# Daily Challenge #26 - Ranking Position

We're back with another week of challenges!

Today, let's get a little creative and develop a ranking system where we can sort points and calculate the position of an individual or a team competing in a game/competition.

Note:
If two or more persons have the same number of points, they should have same position number and sorted by name (name is unique).

For example, Input structure:

[
{
name: "John",
points: 100,
},
{
name: "Bob",
points: 130,
},
{
name: "Mary",
points: 120,
},
{
name: "Kate",
points: 120,
},
]


Output should be:

[
{
name: "Bob",
points: 130,
position: 1,
},
{
name: "Kate",
points: 120,
position: 2,
},
{
name: "Mary",
points: 120,
position: 2,
},
{
name: "John",
points: 100,
position: 4,
},
]


Good luck!

This challenge comes from user kzm. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge for a future post? Email yo+challenge@dev.to with your suggestions!

Posted on by:

### dev.to staff

The hardworking team behind dev.to ❤️

### Discussion

JavaScript

const addRanking = list => {
let prevValue = undefined;
let prevRanking = 1;
list.sort((a,b) => {
if (a.points === b.points) {
if (b.name > a.name) {
return -1;
} else {
return 1;
}
} else {
return b.points - a.points;
}
}).forEach((el, idx) => {
if (prevValue === el.points) {
el.ranking = prevRanking;
} else {
el.ranking = idx + 1;
prevValue = el.points;
prevRanking = el.ranking;
}
});
};


Live demo on Codepen.

This could be considerably reduced by using ternary operators.

Hi alvaro. Could u please explain what u did in this program by putting comments in ur codepen link. Im trying to understand😊

I updated the Codepen with comments. Let me know if more details are needed.

Thanku alvaro😊

-- Data/Score.hs
module Data.Score where

data Score = Score
{ name :: String
, points :: Int
} deriving (Show)

compare_scores :: Score -> Score -> Ordering
compare_scores a b =
case compare (points a) (points b) of EQ -> compare (name b) (name a)
LT -> LT
GT -> GT

-- Data/Rank.hs
module Data.Rank where

import Data.Score as S

data Rank = Rank
{ name :: String
, points :: Int
, position :: Int
} deriving (Show)

from_score :: S.Score -> Int -> Rank
from_score score position = Rank
{ Data.Rank.name = S.name score
, Data.Rank.points = S.points score
, Data.Rank.position = position
}

import Data.List (sortBy)
import Data.Rank as R
import Data.Score as S

sort_scores :: [ S.Score ] -> [ S.Score ]
sort_scores = sortBy (flip S.compare_scores)

map_to_records :: [ S.Score ] -> [ R.Rank ]
map_to_records = foldl kevin []
where
kevin :: [ R.Rank ] -> S.Score -> [ R.Rank ]
kevin [] score = [ R.from_score score 1 ]
kevin xs score
| tied_score = xs ++ [ R.from_score score prev_position ]
| otherwise  = xs ++ [ R.from_score score next_position ]
where
prev = last xs
tied_score = R.points prev == S.points score
prev_position = R.position prev
next_position = (length xs) + 1

rank_scores :: [ S.Score ] -> [ R.Rank ]
rank_scores = map_to_records . sort_scores

scores :: [ S.Score ]
scores =
[ S.Score { S.name = "John"
, S.points =  100
}
, S.Score { S.name = "Bob"
, S.points = 130
}
, S.Score { S.name = "Mary"
, S.points = 120
}
, S.Score { S.name = "Kate"
, S.points = 120
}
]

rank_scores scores
{-[ Rank {name = "Bob", points = 130, position = 1}
, Rank {name = "Kate", points = 120, position = 2}
, Rank {name = "Mary", points = 120, position = 2}
, Rank {name = "John", points = 100, position = 4}
]
-}


Python

r=[
{
'name': "John",
'points': 100,
},
{
'name': "Bob",
'points': 130,
},
{
'name': "Mary",
'points': 120,
},
{
'name': "Kate",
'points': 120,
},
]

def points(e):
return e['points']

p=1
r.sort(reverse= True , key=points)
p_max=r[0]['points']
r[0]['position']= p

for i in range(1,len(r)):
if r[i]['points'] == p_max:     r[i]['position']= p
else:
p+=1
p_max=r[i]['points']
r[i]['position']=p
print(r)


There's probably a better way which requires only one iteration?

const players = [
{
name: "John",
points: 100,
}, {
name: "Bob",
points: 130,
}, {
name: "Mary",
points: 120,
}, {
name: "Kate",
points: 120,
},
];

players.sort((a, b) => {
return (a.points == b.points ? a.name > b.name : a.points < b.points) || -1;
}).forEach((e, i, arr) => {
e.rank = i && arr[i-1].points == e.points ? arr[i-1].rank : i + 1;
});

console.log(players);       // [{"name":"Bob","points":130,"rank":1},{"name":"Kate","points":120,"rank":2},{"name":"Mary","points":120,"rank":2},{"name":"John","points":100,"rank":4}]


i want to contribute my idea with your code. i hope u enjoy with that. Maybe it's shoter

players.sort((a, b) => { return (a.points == b.points ? a.name > b.name : a.points < b.points) || -1; }).map((e,i)=>({...e,position:(i+1)}))

Going functional in Perl:

#!/usr/bin/perl
use warnings;
use strict;

sub rank {
my ($in) = @_; my$i = 1;
my $same = 0; my$last = 'INF';
[ map { if ($_->{points} ==$last) {
++$same; } else {$i += $same;$last = $_->{points};$same = 1;
}
+{ %$_, position =>$i } }
sort { $b->{points} <=>$a->{points}
|| $a->{name} cmp$b->{name}
} @$in ] } my$input = [
{ name => "John", points => 100 },
{ name => "Bob",  points => 130 },
{ name => "Mary", points => 120 },
{ name => "Kate", points => 120 }];

my $expected = [ { name => "Bob", points => 130, position => 1 }, { name => "Kate", points => 120, position => 2 }, { name => "Mary", points => 120, position => 2 }, { name => "John", points => 100, position => 4 }]; use Test::More tests => 1; is_deeply rank($input), \$expected;


Just a short one in Javascript

Edit: somehow I hadn't noticed the position bit 😳, so I've added that as a map call.

ranked = players =>
players
.slice()
.sort((a, b) => Math.sign(b.points - a.points) * 2 + a.name.localeCompare(b.name))
.map((item, index, array) =>
Object.assign(item, {
position: index && item.points == array[index - 1].points ? array[index - 1].position : index + 1,
})
);

players = [
{name: 'b', points: 1},
{name: 'b', points: 3},
{name: 'b', points: 2},
{name: 'a', points: 2},
{name: 'c', points: 2}
];

console.log(JSON.stringify(ranked(players),null,2));
/* ^ [  {
"name": "b",
"points": 3,
"position": 1
},
{
"name": "a",
"points": 2,
"position": 2
},
{
"name": "b",
"points": 2,
"position": 2
},
{
"name": "c",
"points": 2,
"position": 2
},
{
"name": "b",
"points": 1,
"position": 5
}
]
*/


Here it goes!

const players = [
{
name: "John",
points: 100,
},
{
name: "Bob",
points: 130,
},
{
name: "Mary",
points: 120,
},
{
name: "Kate",
points: 120,
},
];

const sortPlayers = (arr) =>
arr.sort((a, b) =>
b.points - a.points
|| a.name.localeCompare(b.name)
)
.map((p, i, l) => Object.assign(p, {position: i && !(l[i-1].points-p.points) ? l[i-1].position : i+1}));


And the result:

sortPlayers(players);
[
{
"name": "Bob",
"points": 130,
"position": 1
},
{
"name": "Kate",
"points": 120,
"position": 2
},
{
"name": "Mary",
"points": 120,
"position": 2
},
{
"name": "John",
"points": 100,
"position": 4
}
]


Shouldn't John's position in the example be 3?