0

I am having difficulty with presenting the top 5 scores from a csv file. This is my current code:

def leaderboards():
    with open('userData.csv', 'r') as data:
        reader = csv.reader(data)
        for idx, row in enumerate(reader):
            print('{} | {} | {}'.format(str(idx), row[0], row[2]))  

This is what I currently get:

>>> leaderboards()
0 | Name | Score
1 | Ryan | 3
2 | Liam | 12
3 | Steve | 3
4 | Donald | 3
5 | Alan | 3
6 | Harry | 1

There are two main elements that I would like to implement but don't know how. They are to sort the score so that the highest score is at the top, and to limit the number of players the leaderboards show to 5.

Current code:

    with open('userData.csv', 'r') as data:
                reader = csv.DictReader(data)
                newList = sorted(reader, key=lambda row: row['Score'],
reverse=False)[0:5]
                print ('  | Name | Score')
                for i, r in enumerate(newList):
                    print('{} | {} | {}'.format(str(i), r['Name'], r['Score']))

Current result:

  | Name | Score
0 | Liam | 12
1 | Harry | 2
2 | Ryan | 3
3 | Steve | 3
4 | Donald | 3

It works for the most part, however, the 2 is higher up than the 3. I'm not really sure why but maybe you might realise something. Once again, thank you so much and I'm sorry that I'm just asking so much but this is out of my league in terms of coding knowledge. :D

Arctic
  • 33
  • 1
  • 7
  • Hello, you are trying to access value [-1] from each row, but as your csv file have empty rows it throws an error. – Anton vBR Sep 17 '18 at 15:42

1 Answers1

1

If you're trying to sort on the last element of each row, the blank space is throwing an error. Also, I think you're script will also consider the header row for sorting, so if you're not looking to consider that, you should skip that row when sorting.

If you want to print out the score the way you want according to your file structure, you could do something like the following:

with open('...file name here...', 'r') as data: reader = csv.reader(data) for idx, row in enumerate(reader): print '{} | {} | {}'.format(str(idx), row[0], row[2])

That will print things out line by line according to how you've indexed your csv. Since you have a header row, I would also suggest that you take a look at DictReader within the Python csv module. That way you won't have to rely on index position in the future.

Also, if you've having issues with extra lines in csv's, take a look at this answer.

Update for sorting based on the request in comments:

To sort your reader you can just use lambda based on the appropriate key in your csv columns. Assuming that Score is the column you wish to sort on and that Score is of index 2, you can do:

newList = sorted(reader, key=lambda row: row[2], reverse=True)

That will give you a sorted list in descending order by the 2nd index. Remove reverse=True for an ascending list order.

Then, if you want to take only the first five entries, you can just slice the list:

slicedList = newList[0:5]

Then, you can print out your list as usual.

Update 2 to accommodate second request:

with open('...name of your file...', 'r') as data:
    reader = csv.reader(data)
    newList = sorted(reader, key=lambda row: row[2], reverse=True)[0:5]
    for i, r in enumerate(newList):
        print('{} | {} | {}'.format(str(i), r[0], r[2]))

The above code assumes (from your original csv file) that r[0] is the "Name" column and r[2] is the "Score" column.

Also, as an alternative, you could use csv.DictReader just in case your column headers change index. The alternative code would be something like:

with open('...your file name here...', 'r') as data:
    reader = csv.DictReader(data)
    #### COMMENTING THIS OUT in favor of the newList below
    # newList = sorted(reader, key=lambda row: row['Score'], reverse=True)[0:5]
    ##### EDIT to evaluate the Score as an int()
    # cast row['Score'] to an int() below so that sorted() treats the row like and int rather than str
    newList = sorted(reader, key=lambda row: int(row['Score']), reverse=True)[0:5]
    print ('  | Name | Score')
    for i, r in enumerate(newList):
        print('{} | {} | {}'.format(str(i), r['Name'], r['Score']))
tlm
  • 952
  • 10
  • 20
  • I have edited my question and I would appreciate if you could take a look at it. Thankyou! :) – Arctic Sep 23 '18 at 10:51
  • @Arctic I updated the answer for you. That should be what your are looking for. – tlm Sep 23 '18 at 14:50
  • Sorry for asking again, but how do I implement this into my code? As the way I am currently doing it outputs this: [['Name', 'Password', 'Score'], ['Ryan', 'pass', '3'], ['Steve', 'pass', '3'], ['Donald', 'adadadad', '3'], ['Alan', 'adadadad', '3']] This isn't ideal as firstly the scores have all been set to the same, and it has lost its format. Thank you so much for your help so far though. – Arctic Sep 23 '18 at 18:26
  • @Arctic I've added a couple of options for you above. – tlm Sep 23 '18 at 19:08
  • I have tried both of your options provided however I have presented the issue in my edit. – Arctic Sep 23 '18 at 20:05
  • @Arctic: I think the `sort` function is evaluating each entry in the Score column as a string. In the line where it says newList = ... try this: `newList = sorted(reader, key=lambda row: int(row['Score'], reverse=True)[0:5]` – tlm Sep 23 '18 at 21:50
  • Awesome, thank you very much for all of your support. I really appreciate it! – Arctic Sep 24 '18 at 17:16