loading...

Daily Challenge #26 - Ranking Position

thepracticaldev profile image dev.to staff ・1 min read

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:

thepracticaldev profile

dev.to staff

@thepracticaldev

The hardworking team behind dev.to ❤️

Discussion

markdown guide
 

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.

 

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

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?