Ranking numbers in lines in a text file from biggest to least?

188
January 19, 2018, at 00:44 AM

I have a text file that looks like:

Josh123: Level 2
Marcus N: Level 4
Callie [L16]: Level 1
Ashley 4: Level 3

What I want is that it will sort each of them by their levels and print it, so like:

Marcus N: Level 4
Ashley 4: Level 3
Josh123: Level 2
Callie [L16]: Level 1

Is there a way to do this? Thank you!

Answer 1

You can try this:

import re
with open('filename.txt') as f:
  data = [i.strip('\n') for i in f]
final_data = '\n'.join(map(lambda x:x[0], sorted(zip(data, [int(re.findall('(?<=Level\s)\d+', i)[0]) for i in data]), key=lambda x:x[-1], reverse=True)))

Output:

Marcus N: Level 4
Ashley 4: Level 3
Josh123: Level 2
Callie [L16]: Level 1
Answer 2

You could go for

import re
string = """
Josh123: Level 2
Marcus N: Level 4
Callie [L16]: Level 1
Ashley 4: Level 3
"""
rx = re.compile(r'.+Level (\d+)')
# create the list of tuples and sort em
sorted_students = sorted([(match.group(0), int(match.group(1))) 
        for match in rx.finditer(string)], 
        key = lambda x: x[1], reverse = True)
# join the first item of every tuple with \n
student = "\n".join(student[0] for student in sorted_students)
print(student)

Which yields

Marcus N: Level 4
Ashley 4: Level 3
Josh123: Level 2
Callie [L16]: Level 1

The idea here is match every line with Level [some digits here], convert the digit and sort by this one afterwards. In the end, join the result with \n back again.

Answer 3

Given:

>>> print(txt)
Josh123: Level 2
Marcus N: Level 4
Callie [L16]: Level 1
Ashley 4: Level 3

You can do:

>>> sorted(txt.splitlines(), key=lambda line: -int(line.split()[-1]))
['Marcus N: Level 4', 'Ashley 4: Level 3', 'Josh123: Level 2', 'Callie [L16]: Level 1']
Answer 4

There are plenty of ways. Here's one simple one:

with open("myFile") as lines:
    print(''.join(reversed(sorted(lines, key = lambda line:int(line.split()[-1])))))

A slightly more robust way:

import re
def getLevel(line):
    match = re.search('Level ([0-9]+)', line)
    if match:
        return int(match.groups()[0])
    return 0
with open("myFile") as lines:
    print(''.join(reversed(sorted(lines, key = getLevel))))

This will put lines that don't match "Level N" at the end. Replace return 0 with return 9999 or the like to put them at the beginning.

Also see Thomas Howard's answer below for a nice alternative.

Answer 5

Another, slightly more robust way, would be to actually build user objects, store them in a list, then sort that. It's extensible, if you add other fields, you can easily build out the namedtuples:

""" Sort players by level
Read a text file, each line represents a user.
Sort these users by level in reversed order.
"""
import re
from collections import namedtuple
User = namedtuple('User', ['level', 'info']) # Named tuple stores user information.
LEVEL_PATTERN = re.compile(r'Level (?P<level>\d+)$') # This finds the users level, compiled only once.
users = [] # A place to hold the users
# Read players in
with open('players.txt') as players:
    for player in players:
        level = re.search(LEVEL_PATTERN, player).group('level')
        users.append(User(level, (player.strip())))
# Sort, and print
for player in sorted(users, key=lambda player: int(player.level), reverse=True): # timeit 2.88, n=1000
    print(player.info)

Best

Edited to use dot-notation and numeric sorting instead of lexicographical.

Also note, the int() part of this snippet can be moved. For example, it can be placed when level is first saved, instead of in the lambda expression:

for player in players:
    level = int(re.search(LEVEL_PATTERN, player).group('level'))
    users.append(User(level, (player.strip())))

Or, when the nampedtuple is created:

level = re.search(LEVEL_PATTERN, player).group('level')
users.append(User(int(level), (player.strip()))) # tiemit 2.06, n=1000

Theoretically, and practically, the above method is faster. On my limited sample it was ~0.8 faster per loop according to timeit.

This makes sense, because doing conversions during comparison can lead to more conversions than if they are done on intake. Taking more time per comparison during the sorting process.

Further, since no sorting algorithm achieves O(n), conversion on intake is the better approach.

Rent Charter Buses Company
READ ALSO
Mixing two layout managers inside the same window in Python (Tkinter)

Mixing two layout managers inside the same window in Python (Tkinter)

I am pretty new to Python and Tkinter, so I hope this question will be easy for the seniors

247
Move Mail item in Outlook with Python

Move Mail item in Outlook with Python

I try to move a message from one Folder in Outlook to another, but it doesn't work and i don't know why

677
Matplotlib xticks values irregular size

Matplotlib xticks values irregular size

I am making a plot with matplotlib and I need an irregular size for xticksI know there is parameter size

375