Difficulty Sorting Tuples with itemgetter - python

I'm new to Python as the screen name attests. I was attempting to sort a list of tuples, think (x,y) pairs in a list and ran into a problem. My goal is to sort the list of tuples by the x variables in ascending order primarily but then sort
I investigated the wiki on HowToSort at http://wiki.python.org/moin/HowTo/Sorting/ and thought I would try the operator module and the itemgetter function as a key.
The simple sorted() function can sort the tuple fine, but when you want one index ascending and one ascending, I'm lost. Here is the code:
from operator import itemgetter, attrgetter
ItemList = [(1,7),(2,1),(1,5),(1,1)]
# Want list sorted with X values descending, then y values ascending
# expected [(2, 1), (1, 1), (1,5), (1, 7)]
print
print ' Input:', ItemList
print 'Output1:',sorted(ItemList, reverse = True)
print
print ' Input:', ItemList
print 'Output2:', sorted(ItemList, key = itemgetter(-0,1))
print
print ' WANTED:', '[(2, 1), (1, 1), (1,5), (1, 7)]'
with the following output:
Input: [(1, 7), (2, 1), (1, 5), (1, 1)]
Output1: [(2, 1), (1, 7), (1, 5), (1, 1)]
Input: [(1, 7), (2, 1), (1, 5), (1, 1)]
Output2: [(1, 1), (1, 5), (1, 7), (2, 1)]
WANTED: [(2, 1), (1, 1), (1, 5), (1, 7)]
I obviously, do not understand the itemgetter function, so any help would be appreciated on that.
Also, any ideas on how to do the two sort on (x,y) pairs? I am hoping to avoid a lambda solution but I'm sure that's where this is going. Thanks.

-0 is the same thing as 0. More-over, negative indices have a different meaning to itemgetter(); it does not mean that the values are negated.
Use a lambda instead:
sorted(ItemList, key=lambda item: (-item[0], item[1]))
Demo:
>>> ItemList = [(1,7),(2,1),(1,5),(1,1)]
>>> sorted(ItemList, key=lambda item: (-item[0], item[1]))
[(2, 1), (1, 1), (1, 5), (1, 7)]
Negative indices take items from the end of a sequence:
>>> end = itemgetter(-1)
>>> end([1, 2, 3])
3
The itemgetter() will never modify the retrieved item, certainly not negate it.
Note that itemgetter() is only a convenience method, you do not have to use it and for more complex sorting orders, a custom function or lambda is the better choice.

Related

How do I solve it? [duplicate]

This question already has answers here:
Sort a list of tuples by 2nd item (integer value) [duplicate]
(9 answers)
Closed 1 year ago.
I am currently trying to solve a problem involving lists and sorting (i.e.) when a list (not a normal one ,but a list of tuple ) is entered ; the program should print out the list but in an orderly(increasing order) manner based on the 2nd elements of each tuples.
ex:
Sample List : [(2, 5), (1, 2), (4, 4), (2, 3), (2, 1)]
Expected Result : [(2, 1), (1, 2), (2, 3), (4, 4), (2, 5)]
Here is what I have tried until now:
def sorting(L):
le=len(L)
G=[]
Lnew=[list(l) for l in L]
for i in range(le):
G.append(Lnew[i][1])
G.sort()
Lnew.remove(Lnew[i][1]) #where the problem is
for k in range(len(G)):
Lnew[k][1]=G[k]
for elt in Lnew:
tuple(elt)
return L
An error displays "list.remove(x): x not in list" .
how do I proceed in that case? or is there a simpler way to tackle the problem ?
from operator import itemgetter
lista_of_tuples = sorted(list_of_tuples, key_itemgetter(0))
itemgetter parameter can be 0,1,2 depending which one to order it by
def sorting(my_list):
return sorted(my_list, key=lambda x: x[1])
sorting([(2, 5), (1, 2), (4, 4), (2, 3), (2, 1)])
Output: [(2, 1), (1, 2), (2, 3), (4, 4), (2, 5)]

Breaking ties in Python sort

I have a list of tuples, each tuple contains two integers. I need to sort the the list (in reverse order) according to the difference of the integers in each tuple, but break ties with the larger first integer.
Example
For [(5, 6), (4, 1), (6, 7)], we should get [(4, 1), (6, 7), (5, 6)].
My way
I have already solved it by making a dictionary that contains the difference as the key and the tuple as the value. But the whole thing is a bit clumsy.
What is a better way?
Use a key function to sorted() and return a tuple; values will be sorted lexicographically:
sorted(yourlst, key=lambda t: (abs(t[0] - t[1])), t[0]), reverse=True)
I'm using abs() here to calculate a difference, regardless of which of the two integers is larger.
For your sample input, the key produces (1, 5), (3, 4) and (1, 6); in reverse order that puts (1, 6) (for the (6, 7) tuple) before (1, 5) (corresponding with (5, 6)).
Demo:
>>> yourlst = [(5, 6), (4, 1), (6, 7)]
>>> sorted(yourlst, key=lambda t: (abs(t[0] - t[1]), t[0]), reverse=True)
[(4, 1), (6, 7), (5, 6)]
Given this list=[('a','b',3),('d','e',3),('e','f',5)], if you'd like to sort by the number in descending order, but break ties (when counts are equal like with the '3' on this example) using ascending alphabetical order of the first element and then the second element repectively, the following code works:
sorted(list,key=lambda x: (-x[2],x[0],x[1]))
Here the '-' sign on the x[2] indicates it needs to be sorted in the descending order.
The output will be: [('e', 'f', 5), ('a', 'b', 3), ('d', 'e', 3)]

sorting nested tuple list

So, what I am trying to do, is to sort a list with that contains (num, tuple) I want to sort it first by the second value of the tuple and if 2 are equal, I want to sort it by the num(of the first tuple).
So lets say I have:
l = [(1,(2,3)),(3,(2,1)),(2,(2,1))]
print(l.sort(key=something))
[(2,(2,1)), (3,(2,1)), (1,(2,3))]
I have tried:
l.sort(key=itemgetter(1,0)
Of course it didn't work. Any ideas?
Thank you.
operator.itemgetter works fine:
>>> from operator import itemgetter
>>> l = [(1,(2,3)),(3,(2,1)),(2,(2,1))]
>>> l.sort(key=itemgetter(1,0))
>>> print(l)
[(2, (2, 1)), (3, (2, 1)), (1, (2, 3))]
>>>
I think the problem is that you tried to print the result of list.sort. You cannot do this because it is an in-place method (it always returns None).
>>> l = [(1,(2,3)),(3,(2,1)),(2,(2,1))]
>>> sorted(l, key=lambda t: (t[1][1],t[0]))
[(2, (2, 1)), (3, (2, 1)), (1, (2, 3))]
Or:
>>> from operator import itemgetter
>>> sorted(l, key=itemgetter(1,0))
[(2, (2, 1)), (3, (2, 1)), (1, (2, 3))]
this works too:
l.sort(key=lambda x: x[::-1])
Note that list.sort sorts the list in place, so print(l.sort(key=something)) would print None.

Python Easiest Way to Sum List Intersection of List of Tuples

Let's say I have the following two lists of tuples
myList = [(1, 7), (3, 3), (5, 9)]
otherList = [(2, 4), (3, 5), (5, 2), (7, 8)]
returns => [(1, 7), (2, 4), (3, 8), (5, 11), (7, 8)]
I would like to design a merge operation that merges these two lists by checking for any intersections on the first element of the tuple, if there are intersections, add the second elements of each tuple in question (merge the two). After the operation I would like to sort based upon the first element.
I am also posting this because I think its a pretty common problem that has an obvious solution, but I feel that there could be very pythonic solutions to this question ;)
Use a dictionary for the result:
result = {}
for k, v in my_list + other_list:
result[k] = result.get(k, 0) + v
If you want a list of tuples, you can get it via result.items(). The resulting list will be in arbitrary order, but of course you can sort it if desired.
(Note that I renamed your lists to conform with Python's style conventions.)
Use defaultdict:
from collections import defaultdict
results_dict = defaultdict(int)
results_dict.update(my_list)
for a, b in other_list:
results_dict[a] += b
results = sorted(results_dict.items())
Note: When sorting sequences, sorted sorts by the first item in the sequence. If the first elements are the same, then it compares the second element. You can give sorted a function to sort by, using the key keyword argument:
results = sorted(results_dict.items(), key=lambda x: x[1]) #sort by the 2nd item
or
results = sorted(results_dict.items(), key=lambda x: abs(x[0])) #sort by absolute value
A method using itertools:
>>> myList = [(1, 7), (3, 3), (5, 9)]
>>> otherList = [(2, 4), (3, 5), (5, 2), (7, 8)]
>>> import itertools
>>> merged = []
>>> for k, g in itertools.groupby(sorted(myList + otherList), lambda e: e[0]):
... merged.append((k, sum(e[1] for e in g)))
...
>>> merged
[(1, 7), (2, 4), (3, 8), (5, 11), (7, 8)]
This first concatenates the two lists together and sorts it. itertools.groupby returns the elements of the merged list, grouped by the first element of the tuple, so it just sums them up and places it into the merged list.
>>> [(k, sum(v for x,v in myList + otherList if k == x)) for k in dict(myList + otherList).keys()]
[(1, 7), (2, 4), (3, 8), (5, 11), (7, 8)]
>>>
tested for both Python2.7 and 3.2
dict(myList + otherList).keys() returns an iterable containing a set of the keys for the joined lists
sum(...) takes 'k' to loop again through the joined list and add up tuple items 'v' where k == x
... but the extra looping adds processing overhead. Using an explicit dictionary as proposed by Sven Marnach avoids it.

Get tuples with max value from each key from a list

I have a list of tuples like this:
[(1, 0), (2, 1), (3, 1), (6, 2), (3, 2), (2, 3)]
I want to keep the tuples which have the max first value of every tuple with the same second value. For example (2, 1) and (3, 1) share the same second (key) value, so I just want to keep the one with the max first value -> (3, 1). In the end I would get this:
[(1, 0), (3, 1), (6, 2), (2, 3)]
I don't mind at all if it is not a one-liner but I was wondering about an efficient way to go about this...
from operator import itemgetter
from itertools import groupby
[max(items) for key, items in groupby(L,key = itemgetter(1))]
It's assuming that you initial list of tuples is sorted by key values.
groupby creates an iterator that yields objects like (0, <itertools._grouper object at 0x01321330>), where the first value is the key value, the second one is another iterator which gives all the tuples with that key.
max(items) just selects the tuple with the maximum value, and since all the second values of the group are the same (and is also the key), it gives the tuple with the maximum first value.
A list comprehension is used to form an output list of tuples based on the output of these functions.
Probably using a dict:
rd = {}
for V,K in my_tuples:
if V > rd.setdefault(K,V):
rd[K] = V
result = [ (V,K) for K,V in rd.items() ]
import itertools
import operator
l = [(1, 0), (2, 1), (3, 1), (6, 2), (3, 2), (2, 3)]
result = list(max(v, key=operator.itemgetter(0)) for k, v in itertools.groupby(l, operator.itemgetter(1)))
You could use a dictionary keyed on the second element of the tuple:
l = [(1, 0), (2, 1), (3, 1), (6, 2), (3, 2), (2, 3)]
d = dict([(t[1], None) for t in l])
for v, k in l:
if d[k] < v:
d[k] = v
l2 = [ (v, k) for (k, v) in d.items() if v != None ]
print l2

Categories

Resources