DEV Community

dev.to staff
dev.to staff

Posted on

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!

Top comments (13)

Collapse
 
alvaromontoro profile image
Alvaro Montoro

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;
    }
  });
};
Enter fullscreen mode Exit fullscreen mode

Live demo on Codepen.

Collapse
 
alvaromontoro profile image
Alvaro Montoro

This could be considerably reduced by using ternary operators.

Collapse
 
nishu1343 profile image
nishu1343

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

Thread Thread
 
alvaromontoro profile image
Alvaro Montoro

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

Thread Thread
 
nishu1343 profile image
nishu1343

Thanku alvaro😊

Collapse
 
avalander profile image
Avalander • Edited

My solution in clumsy Haskell:

-- 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}
  ]
-}
Collapse
 
matrossuch profile image
Mat-R-Such

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)
Collapse
 
wheatup profile image
Hao • Edited

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}]
Collapse
 
ceck profile image
Ceck

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

Collapse
 
choroba profile image
E. Choroba • Edited

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;
Collapse
 
willsmart profile image
willsmart • Edited

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
  }
]
*/
Collapse
 
ynndvn profile image
La blatte • Edited

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
  }
]
Collapse
 
savagepixie profile image
SavagePixie

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