[Tutor] how to invert tuples

Steven D'Aprano steve at pearwood.info
Sun Nov 22 19:39:07 EST 2015


On Sun, Nov 22, 2015 at 09:58:35PM +0100, marcus lütolf wrote:
> dear pythonistas
> 
> i have written the code below for identifying tuples occurring twice 
> or more times in the list of tuples called "flights".
> 1. Question: How can i modify this code so it does not matter which 
> string is first and which is second or (h,i) == (i,h) respectively ?

Modifying your code is perhaps not the best way. It might be better to 
start with something different.

a, b, c, d, e, f, g, h, i, j, k, l = ['M' + str(i) for i in range(1, 13)]
flights = [
    (h,i), (h,j),(k,f), (k,a), (f,a), (e,f), (e,g), (d,g), (l,c),(l,b), 
    (c,b),(j,c), (j,k), (c,k), (h,f), (h,d), (f,d), (a,l), (a,g),(e,i), 
    (e,b,), (i,b), (i,l), (i,k), (l,k), (f,j),(f,b), (j,b),(h,a),(h,g),
    (a,g), (d,e), (d,c), (e,c), (i,c), (i,d), (c,d), (f,e,),(f,i),(e,i), 
    (a,j,), (a,b), (j,b), (h,l), (h,k), (l,k), (l,f), (l,g),(f,g),(b,k), 
    (b,d), (k,d), (i,a), (i,j), (a,j), (h,c), (h,e), (c,e), (h,j)
    ]

# Convert from tuples to sets, the hard way:
tmp = []
for x in flights:
    tmp.append(frozenset(x))
flights = tmp

# Count how many times each frozen set appears.
from collections import Counter
c = Counter(flights)

# Print duplicate keys.
for key, count in c.items():
    if count > 1:
        k = tuple(sorted(key))  # Make it prettier to look at.
        print "%s appears %d times" % (k, count)




There are a couple of tricks here:

- Use the Counter object to do the counting.
- Use sets instead of tuples.

The problem is that tuples (a, b) care about their order. By definition, 
(a, b) is not the same as (b, a). But sets don't care about their order.

We can write sets like {a, b}, which by definition is equal to {b, a}. 
Unfortunately, for technical reasons sets can't be used as the key in 
dicts or Counters. Fortunately, Python has a frozenset type which can. 
So we convert the tuples into frozensets. Above, we do it the hard way. 
Of course, there's an easy way too:

# Convert from tuples to sets the easy way:
flights = [frozenset(x) for x in flights]

*Two* easy ways:

flights = map(frozenset, flights)

You are not expected to understand how they work, but you are welcome to 
ask if you would like it explained.

Unfortunately, frozensets don't have a particularly pleasant looking 
display when printed, for example:

frozenset(['M7', 'M1'])

Not ugly by any means, but rather bulky. So when printing, we convert 
back to a tuple with:

tuple(sorted(key))

The call to sorted() ensures that the two items ('M7' and 'M1', say) 
always appear in consistent order.


But perhaps it would be better to avoid generating the duplicate flights 
in the first place. I'm not sure where your list of flights came from in 
the first place, but we can systematically generate all the unique pairs 
like this:


locations = ['M' + str(i) for i in range(1, 13)]
unique_pairs = []
for i, a in enumerate(locations):
    for b in locations[i+1:]:
        unique_pairs.append((a, b))


> 2. Question: On my python file I have written the rather long list of 
> tuples in 3 lines. If I break them down in multiple shorter lines the 
> for loop will not work through the last tuple.

I'm afraid I don't understand what you mean by this. 



-- 
Steve


More information about the Tutor mailing list