List comprehension to extract multiple fields from list of tuples

Viewed 7388

I have a list of tuples

servers = [('server1', 80 , 1, 2), ('server2', 443, 3, 4)]

I want to create a new list that only has the first two fields as in:

 [('server1', 80), ('server2', 443)]

but I cannot see how to craft a list comprehension for more than one element.

hosts = [x[0] for x in servers]  # this works to give me ['server1', server2']

hostswithports = [x[0], x[1] for x in servers] # this does not work

I prefer to learn the pythonic way vs using a loop - what am I doing wrong?

4 Answers

You can use extended iterable unpacking.

>>> servers = [('server1', 80 , 1, 2), ('server2', 443, 3, 4)]
>>> [(server, port) for server, port, *_ in servers]
[('server1', 80), ('server2', 443)]

Using _ as a throwaway placeholder-name is a common convention.

What you were doing was almost right. You were attempting to translate each tuple in your list into a new tuple. But you forgot to actually declare the tuple. That's what the parentheses are doing:

hosts = [(x[0], x[1]) for x in servers]

Using basic slicing, which has the benefit of not failing if any of your list elements don't have the expected number of sub-elements.

[el[:2] for el in servers]

[('server1', 80), ('server2', 443)]

You could use itemgetter:

from operator import itemgetter


servers = [('server1', 80 , 1, 2), ('server2', 443, 3, 4)]

result = list(map(itemgetter(0, 1), servers))

print(result)

Output

[('server1', 80), ('server2', 443)]

A more readable alternative is the following:

from operator import itemgetter

get_server_and_port = itemgetter(0, 1)
servers = [('server1', 80, 1, 2), ('server2', 443, 3, 4)]
result = [get_server_and_port(e) for e in servers]

print(result)  # [('server1', 80), ('server2', 443)]
Related