Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python descending order from text file

Tags:

python-3.x

At the moment I am sorting my text file by descending order like this:

import operator
fo = open('3a.txt','r')
x = fo.readlines()
sorted_x = sorted(x, key=operator.itemgetter(0),reverse = True)
print(sorted_x)

My text files look like this:

5 Helen 
4 judy
8 Rachel

I was wondering how I would be able to use operator.itemgetter(0) with double figures too?

When I use this with:

5 Helen 
4 judy
25 Hanna
11 Elsa
8 Rachel

The results are all wrong:

['8 Rachel', '5 Helen', '4 judy', '25 Hanna', '11 Elsa']

even if I use operator.itemgetter(0, 1).

like image 859
pythonlegend232 Avatar asked Jun 02 '26 21:06

pythonlegend232


1 Answers

Your operator.itemgetter(0) approach can only get you so far; it'll select the first character of your lines, always.

To properly sort your file numerically, your key function will have to do a few things:

  1. Properly separate out the number at the start of the line from the rest.
  2. Turn the string representing the number into a proper integer value.

The latter is important, because sorting strings is done lexicographically; e.g. '10' is sorted before '9', because 1 comes before 9.

To support an arbitrary run of numeric characters at the start of the line, separated by whitespace, you need to split on whitespace. The str.split() method can do this for you if you don't give it any arguments or use None for the first argument. To keep things efficient, limit the number of splits to just 1, and turn the first element of the result into an integer:

fo = open('3a.txt','r')
x = fo.readlines()
sort_key = lambda line: int(line.split(None, 1)[0])
sorted_x = sorted(x, key=sort_key, reverse=True)
print(sorted_x)

So the key argument is given an anonymous function (a lambda), and that function takes one argument, the line being sorted. That line is split on whitespace just once (line.split(None, 1) and the first element of that split is turned into an integer:

>>> '11 Hello World'.split(None, 1)
['11', 'Hello World']
>>> '11 Hello World'.split(None, 1)[0]
'11'
>>> int('11 Hello World'.split(None, 1)[0])
11

You can improve the rest of your implementation too; there is no need to call file.readlines() as files are iterable. sorted() will take everything in an iterable and sort that, so you can just pass the whole file object directly to the function.

You also want to handle files such that they are closed automatically when done. Make use of the fact that they are context managers; the with statement will signal to them when the context is 'done' (has ended) and file objects will close themselves automatically:

sort_key = lambda line: int(line.split(None, 1)[0])

with open('3a.txt','r') as fo:
    sorted_x = sorted(fo, key=sort_key, reverse=True)

print(sorted_x)

Demo:

>>> from io import StringIO
>>> fo = StringIO('''\
... 5 Helen 
... 4 judy
... 25 Hanna
... 11 Elsa
... 8 Rachel
... '''
... )
>>> sort_key = lambda line: int(line.split(None, 1)[0])
>>> sorted(fo, key=sort_key, reverse=True)
['25 Hanna\n', '11 Elsa\n', '8 Rachel\n', '5 Helen \n', '4 judy\n']

You could accomplish this and still use operator.itemgetter(0) only if you first split your lines up into lists, where element 0 is an integer number:

import operator

sort_key = operator.itemgetter(0)

with open('3a.txt','r') as fo:
    split_lines = (line.split(None, 1) for line in fo)
    numeric_lines = ((int(line[0]), line[1]) for line in split_lines)
    sorted_x = sorted(numeric_lines, key=sort_key, reverse=True)

print(sorted_x)

This makes use of generator expressions to process the lines as they are being read. However now you have a list with each element a tuple, an integer and the rest of your line:

>>> import operator
>>> fo = StringIO('''\
... 5 Helen 
... 4 judy
... 25 Hanna
... 11 Elsa
... 8 Rachel
... ''')
>>> sort_key = operator.itemgetter(0)
>>> split_lines = (line.split(None, 1) for line in fo)
>>> numeric_lines = ((int(line[0]), line[1]) for line in split_lines)
>>> sorted(numeric_lines, key=sort_key, reverse=True)
[(25, 'Hanna\n'), (11, 'Elsa\n'), (8, 'Rachel\n'), (5, 'Helen \n'), (4, 'judy\n')]
like image 68
Martijn Pieters Avatar answered Jun 04 '26 11:06

Martijn Pieters



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!