How do I alphabetically sort every -n line in a text file, and have the remaining information follow along?
Question:
I am trying to sort a text file in alphabetical order (names) but I still want the information regarding the specific persons to follow with the reordering. I know it sounds weird, but an example might help.
Every person has two names, a phone number, en email address, and an address. When I sort the file, I want the information about every specific person to follow with them.
Text file:
Bob Steven
02847661723
[email protected]
Bob Street 121
Alex Ericsson
1233213211
[email protected]
Alex Street 112
Chris Philips
018207129387
[email protected]
Christ Street 902
When sorted, it should be something like this:
Alex Ericsson
1233213211
[email protected]
Alex Street 112
Bob Steven
02847661723
[email protected]
Bob Street 121
Chris Philips
018207129387
[email protected]
Christ Street 902
I found this string of code which kind of works, but it sorts every single line. How do I make it so it only sorts every 4th line (1st line, 5th line and so on)?
input_filename = "filename.txt"
output_filename = "filename.txt"
with open(input_filename, "r") as f:
l = [line for line in f if line.strip()]
l.sort(key=lambda line: line.split())
with open(output_filename, "w") as f:
for line in l:
f.write(line)
Answers:
You can try:
with open("input.txt", "r") as f_in:
data = [line for line in map(str.strip, f_in) if line]
data = sorted(
[data[i : i + 4] for i in range(0, len(data), 4)], key=lambda k: k[0]
)
with open("output.txt", "w") as f_out:
for chunk in data:
print(*chunk, file=f_out, sep="n")
This creates output.txt
with contents:
Alex Ericsson
1233213211
[email protected]
Alex Street 112
Bob Steven
02847661723
[email protected]
Bob Street 121
Chris Philips
018207129387
[email protected]
Christ Street 902
s = """Bob Steven
02847661723
[email protected]
Bob Street 121
Alex Ericsson
1233213211
[email protected]
Alex Street 112
Chris Philips
018207129387
[email protected]
Christ Street 90"""
data = 'n'.join(['n'.join(x) for x in sorted([s.split('n')[i:i+4] for i in range(0, len(s.split('n')), 4)], key=lambda x: x[0])])
print(data)
output:
Alex Ericsson
1233213211
[email protected]
Alex Street 112
Bob Steven
02847661723
[email protected]
Bob Street 121
Chris Philips
018207129387
[email protected]
Christ Street 90
Basically, what want to do here is read your file in chunks, and then sort each chunk. Andrej has already demonstrated how to do that, but let’s take it up a notch. Let’s define a class to encapsulate the data for each person. The class contains attributes name
, phone
, email
, address
to store each of our data fields, and methods __str__
to convert the class back to a string (to print to screen/file), and __eq__
and __gt__
to define comparison to other objects of the class (to sort). We can also define a class method from_iter
that will take in a file handle (or other iterator) and consume its first four elements to define a Person
object:
def Person:
def __init__(self, name, phone, email, address):
self.name = name
self.phone = phone
self.email = email
self.address = address
@classmethod
def from_iter(cls, src):
return Person(next(src), next(src), next(src), next(src))
def _as_tuple(self):
return (self.name, self.phone, self.email, self.address)
def __repr__(self):
return f"Person({repr(self.name)}, {repr(self.phone)}, {repr(self.email)}, {repr(self.address)})"
def __str__(self):
return "n".join(self._as_tuple())
def __eq__(self, other):
return self._as_tuple() == other._as_tuple()
def __gt__(self, other):
return self._as_tuple() > other._as_tuple()
Now, we can open the file and pass the handler to the reader function:
people = []
with open(input_filename) as f_in:
try:
people.append(Person.from_iter(f_in))
except StopIteration: # Thrown when the file is exhausted
pass
Now, we have the following people
list:
[Person('Bob Steven', '02847661723', '[email protected]', 'Bob Street 121'),
Person('Alex Ericsson', '1233213211', '[email protected]', 'Alex Street 112'),
Person('Chris Philips', '018207129387', '[email protected]', 'Christ Street 902')]
Sorting this list is easy — use the sorted
function, and since we already defined the __eq__
and __gt__
methods, the class can figure out how its objects compare to each other. In this case, the _as_tuple()
of each object is compared, which simply compares the names first, then the phones, then the emails, and finally the addresses of the two objects. Simply doing sorted(people)
gives
[Person('Alex Ericsson', '1233213211', '[email protected]', 'Alex Street 112'),
Person('Bob Steven', '02847661723', '[email protected]', 'Bob Street 121'),
Person('Chris Philips', '018207129387', '[email protected]', 'Christ Street 902')]
And to write it back to a file:
sorted_people = sorted(people)
with open(output_filename, "w") as f_out:
for p in sorted_people:
f_out.write(str(p)) # Use the __str__ defined in the class
f_out.write("n") # Because a newline needs to be explicitly written
we get the desired output:
Alex Ericsson
1233213211
[email protected]
Alex Street 112
Bob Steven
02847661723
[email protected]
Bob Street 121
Chris Philips
018207129387
[email protected]
Christ Street 902
I am trying to sort a text file in alphabetical order (names) but I still want the information regarding the specific persons to follow with the reordering. I know it sounds weird, but an example might help.
Every person has two names, a phone number, en email address, and an address. When I sort the file, I want the information about every specific person to follow with them.
Text file:
Bob Steven
02847661723
[email protected]
Bob Street 121
Alex Ericsson
1233213211
[email protected]
Alex Street 112
Chris Philips
018207129387
[email protected]
Christ Street 902
When sorted, it should be something like this:
Alex Ericsson
1233213211
[email protected]
Alex Street 112
Bob Steven
02847661723
[email protected]
Bob Street 121
Chris Philips
018207129387
[email protected]
Christ Street 902
I found this string of code which kind of works, but it sorts every single line. How do I make it so it only sorts every 4th line (1st line, 5th line and so on)?
input_filename = "filename.txt"
output_filename = "filename.txt"
with open(input_filename, "r") as f:
l = [line for line in f if line.strip()]
l.sort(key=lambda line: line.split())
with open(output_filename, "w") as f:
for line in l:
f.write(line)
You can try:
with open("input.txt", "r") as f_in:
data = [line for line in map(str.strip, f_in) if line]
data = sorted(
[data[i : i + 4] for i in range(0, len(data), 4)], key=lambda k: k[0]
)
with open("output.txt", "w") as f_out:
for chunk in data:
print(*chunk, file=f_out, sep="n")
This creates output.txt
with contents:
Alex Ericsson
1233213211
[email protected]
Alex Street 112
Bob Steven
02847661723
[email protected]
Bob Street 121
Chris Philips
018207129387
[email protected]
Christ Street 902
s = """Bob Steven
02847661723
[email protected]
Bob Street 121
Alex Ericsson
1233213211
[email protected]
Alex Street 112
Chris Philips
018207129387
[email protected]
Christ Street 90"""
data = 'n'.join(['n'.join(x) for x in sorted([s.split('n')[i:i+4] for i in range(0, len(s.split('n')), 4)], key=lambda x: x[0])])
print(data)
output:
Alex Ericsson
1233213211
[email protected]
Alex Street 112
Bob Steven
02847661723
[email protected]
Bob Street 121
Chris Philips
018207129387
[email protected]
Christ Street 90
Basically, what want to do here is read your file in chunks, and then sort each chunk. Andrej has already demonstrated how to do that, but let’s take it up a notch. Let’s define a class to encapsulate the data for each person. The class contains attributes name
, phone
, email
, address
to store each of our data fields, and methods __str__
to convert the class back to a string (to print to screen/file), and __eq__
and __gt__
to define comparison to other objects of the class (to sort). We can also define a class method from_iter
that will take in a file handle (or other iterator) and consume its first four elements to define a Person
object:
def Person:
def __init__(self, name, phone, email, address):
self.name = name
self.phone = phone
self.email = email
self.address = address
@classmethod
def from_iter(cls, src):
return Person(next(src), next(src), next(src), next(src))
def _as_tuple(self):
return (self.name, self.phone, self.email, self.address)
def __repr__(self):
return f"Person({repr(self.name)}, {repr(self.phone)}, {repr(self.email)}, {repr(self.address)})"
def __str__(self):
return "n".join(self._as_tuple())
def __eq__(self, other):
return self._as_tuple() == other._as_tuple()
def __gt__(self, other):
return self._as_tuple() > other._as_tuple()
Now, we can open the file and pass the handler to the reader function:
people = []
with open(input_filename) as f_in:
try:
people.append(Person.from_iter(f_in))
except StopIteration: # Thrown when the file is exhausted
pass
Now, we have the following people
list:
[Person('Bob Steven', '02847661723', '[email protected]', 'Bob Street 121'),
Person('Alex Ericsson', '1233213211', '[email protected]', 'Alex Street 112'),
Person('Chris Philips', '018207129387', '[email protected]', 'Christ Street 902')]
Sorting this list is easy — use the sorted
function, and since we already defined the __eq__
and __gt__
methods, the class can figure out how its objects compare to each other. In this case, the _as_tuple()
of each object is compared, which simply compares the names first, then the phones, then the emails, and finally the addresses of the two objects. Simply doing sorted(people)
gives
[Person('Alex Ericsson', '1233213211', '[email protected]', 'Alex Street 112'),
Person('Bob Steven', '02847661723', '[email protected]', 'Bob Street 121'),
Person('Chris Philips', '018207129387', '[email protected]', 'Christ Street 902')]
And to write it back to a file:
sorted_people = sorted(people)
with open(output_filename, "w") as f_out:
for p in sorted_people:
f_out.write(str(p)) # Use the __str__ defined in the class
f_out.write("n") # Because a newline needs to be explicitly written
we get the desired output:
Alex Ericsson
1233213211
[email protected]
Alex Street 112
Bob Steven
02847661723
[email protected]
Bob Street 121
Chris Philips
018207129387
[email protected]
Christ Street 902