Sorting a list using two keys - python

I have a list of strings which i want to sort according to number of dots(.) in the given string and if the number of dots in those strings is equal i want them to be sorted by their length.(Both in descending order should not be a problem anyway)
The first part could be implemented easily
given_list.sort(key=dots,reverse=True)
dots function is implemented and it works fine.
This is where i am stuck as I am unable to sort the already sorted list according to their lengths if the number of dots is equal.
Which leads me to think i should somehow customize the key parameter using lambda, but it does not really work as it should , but it does in the case of nested lists or dictionaries .
how do i get this done?

You can pass in a custom lambda function to the sorted method as the sorting key.
In this case, I use a lambda x: (x.count("."), len(x)), which will create a composite key of count and length, and sort accordingly:
>>> given_list = ["e.x.a", "m.p.l.e", "ex.am.ple"]
>>> sorted(given_list, key=lambda x: (x.count("."), len(x)))
['e.x.a', 'ex.am.ple', 'm.p.l.e']
>>> sorted(given_list, key=lambda x: (x.count("."), len(x)), reverse=True)
['m.p.l.e', 'ex.am.ple', 'e.x.a']
Since you are using .sort, you can do the same here as well to sort the list in-place:
>>> given_list = ["e.x.a", "m.p.l.e", "ex.am.ple"]
>>> given_list.sort(key=lambda x: (x.count("."), len(x)), reverse=True)
>>> given_list
['m.p.l.e', 'ex.am.ple', 'e.x.a']

Related

How to sort a list containing frozensets (python)

I have a list of frozensets that I'd like to sort, Each of the frozensets contains a single integer value that results from an intersection operation between two sets:
k = frozenset(w) & frozenset(string.digits)
d[k] = w # w is the value
list(d) # sorted(d) doesn't work since the keys are sets and sets are unordered.
Here is the printed list:
[frozenset({'2'}), frozenset({'1'}), frozenset({'4'}), frozenset({'3'})]
How can I sort the list using the values contained in the sets?
You need to provide function as key to sorted which would accept frozenset as argument and return something which might be compared. If each frozenset has exactly 1 element and said element is always single digit then you might use max function (it will extract that single element, as sole element is always biggest element of frozenset) that is
d1 = [frozenset({'2'}), frozenset({'1'}), frozenset({'4'}), frozenset({'3'})]
d2 = sorted(d1,key=max)
print(d2)
output
[frozenset({'1'}), frozenset({'2'}), frozenset({'3'}), frozenset({'4'})]
If you want to know more read Sorting HOW TO
Previous answers can not sorted correctly, Because of strings
d = [frozenset({'224'}), frozenset({'346'}), frozenset({'2'}), frozenset({'22345'})]
sorted(d, key=lambda x: int(list(x)[0]))
Output:
[frozenset({'2'}),
frozenset({'224'}),
frozenset({'346'}),
frozenset({'22345'})]
Honestly, unless you really need to keep the elements as frozenset, the best might be to generate a list of values upstream ([2, 1, 4, 3]).
Anyway, to be able to sort the frozensets you need to make them ordered elements, for instance by converting to tuple. You can do this transparently using the key parameter of sorted
l = [frozenset({'2'}), frozenset({'1'}), frozenset({'4'}), frozenset({'3'})]
sorted(l, key=tuple)
or natsorted for strings with multiple digits:
from natsort import natsorted
l = [frozenset({'2'}), frozenset({'1'}), frozenset({'14'}), frozenset({'3'})]
natsorted(l, key=tuple)
output:
[frozenset({'1'}), frozenset({'2'}), frozenset({'3'}), frozenset({'14'})]

python sort list by substr

I would like to sort a list by a substr of the contents.
Imaginbe the following list and I would like it to be sorted by the number after the '-':
>>> lst = ['ABC-789','DEF-123','GHI-456']
>>> sorted(lst,key=lambda x=lst.split('-') x[1])
This gives me:
sorted(lst, key=lambda x=lst.split('-');x[1])
^
SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
How can I achieve this?
This should work:
sorted(lst, key=lambda x: int(x.split('-')[1]))
Output:
['DEF-123', 'GHI-456', 'ABC-789']
Consider this corrected version:
lst = ['ABC-789','DEF-123','GHI-456']
lst = sorted(lst,key=lambda x: int(x.split('-')[1]))
print(lst) # ['DEF-123', 'GHI-456', 'ABC-789']
You had two issues here. First, your lambda syntax was off, and you want lambda x: <expr in x>. Second, since you want to sort numerically, after extracting the string to the right of the hyphen, you also should be casting to integer. Coincidentally, you can get away with this now, because all numbers are the same text width (3 digits). But, should the numbers not all be the same width, sorting by text might not give a numerical sort.
lst = ['ABC-789','DEF-123','GHI-456']
lst.sort(key=lambda x:x.split('-')[1])
print(lst)
Corrected the lambda and split syntax. Also, I have used the list.sort method instead of sorted in case the original list needs to be sorted and changed.

Converting List into Dictionary of Sorted Tuples

I've used the following approach many times to generate a list of tuples from the contents of a dictionary:
dispositions = list(dispositions.items())
In this case, the keys are different types of ways a patient can leave the emergency department, and the values are counts of those types. Now, I wanted to sort this data based on the second item in each of the tuples, so I tried this:
dispositions = list(dispositions.items()).sort(key=lambda x: x[1])
To my surprise, when I ran the code, I found that dispositions had been set to None. I tried breaking it into two parts, as follows:
dispositions = list(dispositions.items())
dispositions.sort(key=lambda x: x[1])
This works! I got my list of sorted tuples. So, I've already solved my problem, but I want to know why the first option didn't work so that I can be a better person (better programmers are better people). Can anyone help me out here?
list.sort does not return anything. So this:
dispositions = list(dispositions.items()).sort(key=lambda x: x[1])
sets dispositions to None.
sorted on the other hand returns a new list. So you would use:
dispositions = sorted(dispositions.items(), key=lambda x: x[1])
Which will set dispositions to a sorted list of tuples.
The sort function doesn't work like you might think. It does not return a sorted list, it directly modifies the list so it's sorted.
For instance, say you have this code:
a = [0, 2, 1]
a.sort()
a will now be changed [0, 1, 2]. Had I used a = a.sort() rather than just a.sort(), it would have been set to None.
This:
dispositions = list(dispositions.items()).sort(key=lambda x: x[1])
functions the same as this:
dispositions = list(dispositions.items())
dispositions = dispositions.sort(key=lambda x: x[1])
Which sets dispositions to the returned value from sort, which is None.
On the other hand, this:
dispositions = list(dispositions.items())
dispositions.sort(key=lambda x: x[1])
is not setting dispositions to the value of sort, it's simply calling the sort function on dispositions, which causes the list to be sorted as intended.

How to sort a dictionary by two elements, reversing only one

Consider the following dictionary:
data = {'A':{'total':3},
'B':{'total':5},
'C':{'total':0},
'D':{'total':0},
}
The desired order for above is B, A, C, D. Order by descending total, then by ascending key.
When I call sorted(data, key=lambda x: (data[x]['total'], x), reverse=True)
I get B,A,D,C because reverse is called on both keys.
Is there an efficient way to solve this?
Sort on negative total, that'll reverse put the totals in reverse order without having to use reverse=True. Ties are then broken on the key in forward order:
sorted(data, key=lambda x: (-data[x]['total'], x))
Demo:
>>> data = {'A':{'total':3},
... 'B':{'total':5},
... 'C':{'total':0},
... 'D':{'total':0},
... }
>>> sorted(data, key=lambda x: (-data[x]['total'], x))
['B', 'A', 'C', 'D']
This trick only works for numeric components in a sort key; if you have multiple keys that require a sort direction change that are not numeric, you'd have to do a multi-pass sort (sort multiple times, from last key to first):
# when you can't take advantage of numerical values to reverse on
# you need to sort repeatedly from last key to first.
# Here, sort forward by dict key, then in reverse by total
bykey = sorted(data)
final = sorted(bykey, key=lambda x: data[x]['total'], reverse=True)
This works because the Python sort algorithm is stable; two elements keep their relative positions if the current sort key result is equal for those two elements.

Sorting a dict with tuples as values

I have a dictionary that looks like this:
{'key_info': (rank, raw_data1, raw_data2),
'key_info2': ...}
Basically I need back a list of the keys in sorted order, that is sorted based on the rank field in the tuple.
My code looks something like this right now (diffs is the name of the dict above):
def _sortRanked(self):
print(type(self.diffs))
return sorted(self.diffs.keys(), key=lambda x: x[1], reverse=True)
that right now returns this when I run it:
return sorted(self.diffs.keys(), key=lambda x: x[1], reverse=True)
IndexError: string index out of range
keys() only gives you keys, not values, so you have to use the keys to retrieve values from the dict if you want to sort on them:
return sorted(self.diffs.keys(), key=lambda x: self.diffs[x], reverse=True)
Since you're sorting on rank, which is the first item in the tuple, you don't need to specify which item in the value tuple you want to sort on. But if you wanted to sort on raw_data1:
return sorted(self.diffs.keys(), key=lambda x: self.diffs[x][1], reverse=True)
You're passing the key as the argument to, uh, key.
[k for (k, v) in sorted(D.iteritems(), key=lambda x: x[1], reverse=True)]
You're attempting to sort on the keys of the dictionary, not the values. Replace your self.diffs.keys() call with self.diffs.items(), and then it should work (but do keep the lambda, or use operator.itemgetter(1). Tuples sort starting with the first element, so you don't have to worry about that.)
Just noticed that you only want the keys. With my suggestion, you'd have to wrap the sort with zip()[0] (making sure to unpack the resultant list of tuples from the sort by prefixing with * in the call to zip()).
You're close. Try this instead:
return sorted(self.diffs.keys(), key = lambda x: self.diffs[x][0], reverse = True)
You're sorting a list of keys, so you have to take that key back to the dictionary and retrieve element 1 in order to use it as a comparison value.

Categories

Resources