order = ['w','x','a','z']
[(object,'a'),(object,'x'),(object,'z'),(object,'a'),(object,'w')]
How do I sort the above list of tuples by the second element according the the key list provided by 'order'?
UPDATE on 11/18/13:
I found a much better approach to a variation of this question where the keys are certain to be unique, detailed in this question: Python: using a dict to speed sorting of a list of tuples.
My above question doesn't quite apply because the give list of tuples has two tuples with the key value of 'a'.
You can use sorted, and give as the key a function that returns the index of the second value of each tuple in the order list.
>>> sorted(mylist,key=lambda x: order.index(x[1]))
[('object', 'w'), ('object', 'x'), ('object', 'a'), ('object', 'a'), ('object', 'z')]
Beware, this fails whenever a value from the tuples is not present within the order list.
Edit:
In order to be a little more secure, you could use :
sorted(mylist,key=lambda x: x[1] in order and order.index(x[1]) or len(order)+1)
This will put all entries with a key that is missing from order list at the end of the resulting list.
Related
Hi I am new to python and wanted to sort these values based on the numeric values present in each tuple
b={('shoe',0.98),('bag',0.67),('leather',0.77)}
I have tried changing it into a list but then the tuple elements cannot be changed
Thanks in advance
Python sets are unordered, so you can’t sort them. But you can sort the elements in a set using the sorted function and passing a lambda that selects the second item of a tuple (since the set elements are tuples and you want to sort by the second elements of the tuples) to the key parameter. This returns a list:
out = sorted(b, key=lambda x:x[1])
Output:
[('bag', 0.67), ('leather', 0.77), ('shoe', 0.98)]
Or you can use operator.itemgetter (there is also attrgetter):
from operator import itemgetter
out = sorted(b, key=itemgetter(1))
I have a list like this:
list_results=[('Horror', 2), ('Romance', 2), ('Comedy', 2), ('History', 2), ('Adventure', 1), ('Action', 3)]
I wish to sort the number in descending order and if numbers were the same, according to the name in ascending order.
I tried the following code:
sortlist=sorted(list_results,key=lambda x:(x[1],x[0]))
and the reverse but I couldn't figure out to do it.
The answer that I'm looking for is:
[('Action', 3), ('Comedy', 2) ,('History', 2),('Horror', 2), ('Romance', 2), ('Adventure', 1), ]
First sort the list by the first item, then by the second item:
list_results = sorted(list_results, key=lambda x:x[0])
list_results = sorted(list_results, key=lambda x:x[1], reverse=True)
or better yet without copying:
import operator
list_results.sort(key=operator.itemgetter(0))
list_results.sort(key=operator.itemgetter(1), reverse=True)
Python's sort algorithm is Timsort. It is a stable algorithm meaning if 2 values are the same, they'll stay in their original order.
If you sort alphabetically first, and then by the priority, the list will be sorted according to the alphabet, then re-sorted according to priority with alphabet being secondary.
You want to sort according to two criteria, with one criterion acting as a tie-breaker for the other. Since python's sorted and list.sort are guaranteed to be stable sorts, one solution is to sort the list twice: first sort it by the tie-breaker, then sort it by the main criterion. This is #Bharel's answer.
Another possibility is to sort only once, using a tuple as the key. Python's sorted and list.sort both offer a reverse= True or False argument to specify a sort in increasing or decreasing order; but in your case, we want to sort in decreasing order with respect to the first criterion, and increasing order with respect to the second criterion. The reverse keyword is not helpful because it is all-or-nothing: it will not allow us to choose which criterion to reverse.
Since the first criterion is numeric (an integer), a simple trick to sort in reverse order is to negate it with a minus sign:
sortlist = sorted(list_results, key=lambda x:(-x[1], x[0]))
Note the -x[1] instead of just x[1].
Here are two arguments in favour of sorting once by a tuple, rather than twice:
When sorting according to (-x[1], x[0]), it is immediately clear that -x[1] is the main criterion, and x[0] is only a tie-breaker. By contrast, if you sort twice, someone reading your code needs to take a second to understand that the last sort is the most important, and the previous sort is only there as a tie-breaker relying on sorted being a stable sort.
If the list is long, sorting once with a tuple key is probably faster than sorting twice with simple keys. This is especially true because the second key is a string; comparing string is slower than comparing integers. If you use tuples, the strings will only be compared for two items who are ex aequo on the first key; but if you sort twice, about n log(n) string comparisons will be performed in the first sort.
If your list is small, it probably doesn't matter which version is faster (unless you're repeatedly sorting lots of small lists...), so it's a matter of preference and readability.
I have a dictionary
params = ImmutableMultiDict([('dataStore', 'tardis'), ('symbol', '1'), ('symbol', '2')])
I want to be able to iterate through the dictionary and get a list of all the values and their keys. However, when I try to do it, it only gets the first symbol key value pair and ignores the other one.
for k in params:
print(params.get(k))
If I understand you correctly you want to iterate over all keys, including duplicates, right? Then you could use the items(multi=False) method with multi set to True.
Documentation:
items(multi=False)
Return an iterator of (key, value) pairs.
Parameters: multi – If set to True the iterator returned will have a pair for each value of each key. Otherwise it will only contain
pairs for the first value of each key.
If I misunderstood you and you want a list of all entries to a single key have a look at jonrsharpe's answer.
If you read the docs for MultiDict, from which ImmutableMultiDict is derived, you can see:
It behaves like a normal dict thus all dict functions will only return the first value when multiple values for one key are found.
However, the API includes an additional method, .getlist, for this purpose. There's an example of its use in the docs, too:
>>> d = MultiDict([('a', 'b'), ('a', 'c')])
# ...
>>> d.getlist('a')
['b', 'c']
I am getting the following error on console
newList = sorted(l,key=sort)
NameError: name 'sort' is not defined
after executing the following piece of code:
if __name__ == '__main__':
l= [[input(), float(input())] for _ in range(int(input()))]
print( l)
new = sorted(l,key=sort)
print(new)
From the given article I learned that key parameter in sorted() can use user/inbuilt Python methods. I am trying to sort my list alphabetically so I passed key=sort with an understanding that it will sort my list alphabetically.
Please help me out where am I going wrong here.
sorted() function has an optional parameter called key which takes a function as its value. This key function transforms each element before sorting, it takes the value and returns 1 value which is then used within sort instead of the original value.
Example:
If there is a list of tuples,
li = [('p1', 20), ('p2', 10), ('p3', 30)]
and you want to sort the elements of the list in such a way that the resulting order is as follows:
Ouptut: li = [('p2', 10), ('p1', 20), ('p3', 30)]
Then, all you need to do is, sort the tuples, based on the 2nd element in each tuple.
To do so, we need to use a custom method, which will be applied to each element and the elements will be sorted based on the representative of each element(i.e KEY).
Hence, the syntax to the same will be as follows:
sorted(li, key=lambda x: x[1])
Edit:
Sort is in itself a function, which is used to arrange elements in a specific order. But, it cannot be used to extract a representative (i.e a KEY) for each element in the list.
Well, sort is not a user/built-in Python function. It is a method on lists, but, not a name accessible globally. Even if you would use it (through key=list.sort), it would not give you your desired result, as the key function is used to transform each element before sorting.
By default (without specifying the key parameter), sorted should already sort strings alphabetically (sort of), same with lists containing lists with a string as the first item. If you want to sort "A" and "a" together (as well as "Z" and "z"), you could use the key function str.lower, but you would need to apply that only to the first element of your inner lists:
new = sorted(l, key=lambda x: x[0].lower())
I have a list of tuples of k elements. I'd like to sort with respect to element 0, then element 1 and so on and so forth. I googled but I still can't quite figure out how to do it. Would it be something like this?
list.sort(key = lambda x : (x[0], x[1], ...., x[k-1])
In particular, I'd like to sort using different criteria, for example, descending on element 0, ascending on element 1 and so on.
Since python's sort is stable for versions after 2.2 (or perhaps 2.3), the easiest implementation I can think of is a serial repetition of sort using a series of index, reverse_value tuples:
# Specify the index, and whether reverse should be True/False
sort_spec = ((0, True), (1, False), (2, False), (3, True))
# Sort repeatedly from last tuple to the first, to have final output be
# sorted by first tuple, and ties sorted by second tuple etc
for index, reverse_value in sort_spec[::-1]:
list_of_tuples.sort(key = lambda x: x[index], reverse=reverse_value)
This does multiple passes so it may be inefficient in terms of constant time cost, but still O(nlogn) in terms of asymptotic complexity.
If the sort order for indices is truly 0, 1... n-1, n for a list of n-sized tuples as shown in your example, then all you need is a sequence of True and False to denote whether you want reverse or not, and you can use enumerate to add the index.
sort_spec = (True, False, False, True)
for index, reverse_value in list(enumerate(sort_spec))[::-1]:
list_of_tuples.sort(key = lambda x: x[index], reverse=reverse_value)
While the original code allowed for the flexibility of sorting by any order of indices.
Incidentally, this "sequence of sorts" method is recommended in the Python Sorting HOWTO with minor modifications.
Edit
If you didn't have the requirement to sort ascending by some indices and descending by others, then
from operator import itemgetter
list_of_tuples.sort(key = itemgetter(1, 3, 5))
will sort by index 1, then ties will be sorted by index 3, and further ties by index 5. However, changing the ascending/descending order of each index is non-trivial in one-pass.
list.sort(key = lambda x : (x[0], x[1], ...., x[k-1])
This is actually using the tuple as its own sort key. In other words, the same thing as calling sort() with no argument.
If I assume that you simplified the question, and the actual elements are actually not in the same order you want to sort by (for instance, the last value has the most precedence), you can use the same technique, but reorder the parts of the key based on precedence:
list.sort(key = lambda x : (x[k-1], x[1], ...., x[0])
In general, this is a very handy trick, even in other languages like C++ (if you're using libraries): when you want to sort a list of objects by several members with varying precedence, you can construct a sort key by making a tuple containing all the relevant members, in the order of precedence.
Final trick (this one is off topic, but it may help you at some point): When using a library that doesn't support the idea of "sort by" keys, you can usually get the same effect by building a list that contains the sort-key. So, instead of sorting a list of Obj, you would construct then sort a list of tuples: (ObjSortKey, Obj). Also, just inserting the objects into a sorted set will work, if they sort key is unique. (The sort key would be the index, in that case.)
So I am assuming you want to sort tuple_0 ascending, then tuple_1 descending, and so on. A bit verbose but this is what you might be looking for:
ctr = 0
for i in range(list_of_tuples):
if ctr%2 == 0:
list_of_tuples[0] = sorted(list_of_tuples[0])
else:
list_of_tuples[0] = sorted(list_of_tuples[0], reverse=True)
ctr+=1
print list_of_tuples