python: sort a nested dictionary - python

I have a nested dictionary of type following
{id_1 : [ {id_2:score},{id_3:score_2} .... and so on],id_2 :[{id_1:Score_!....]}
so basically a nested dictionary
Now I want to sort this dictionary per primary id on the basis of score
so basically
{id_1: [{element with max_score},{element_with next max_score....}]... id_2:[{element_with max_score},{element_with next maxx score}...]
also, the function should take an argument say (n) which returns the top n matches or if the n< number of elements of that id then it returns teh complete list
any Ideas/ thoughts..
Thanks

You can use the key parameter to list.sort(). Assuming the outer dictionary is called d, the code could look like this:
for scores in d.itervalues():
scores.sort(key=lambda x: next(x.itervalues()), reverse=True)
The lambda function simply extracts the single value of the dictionary.
I think you'd be better off using tuples instead of dictionaries as the values of your list:
{id_1: [(id_2, score_2), (id_3, score_3),...], id_2: [(id_1, score_1),...]}
Using this data structure, the sorting code would be
for scores in d.itervalues():
scores.sort(key=lambda x: x[1], reverse=True)
or equivalently, but slightly faster
for scores in d.itervalues():
scores.sort(key=operator.itemgetter(1), reverse=True)

Related

How do I sort lists by two different values within that list?

I have a list that is of the following form:
my_list= [['A',(3,4)],['A2',(6,11)],['U1',(2,9)],['P9',(1,9)], ['X',(10,4)]...]
I need to sort the letter/number combinations based on the list that corresponds with them, (1,2,3,4) for example.
The second number in the list needs to be in descending order. Then, the first number in that list needs to be in descending order. The last number takes priority over the first number.
These numbers correspond to the location of these values on an image. I am attempting to sort these by the way they appear on the image (top to bottom, left to right).
The correct order for the above list would be:
['A2',(6,11)], ['U1',(2,9)],['P9',(1,9)], ['X',(10,4)], [['A',(3,4)]
To be frank, I do not know where to start with this. Could someone please explain how to properly write this in Python?
You can pass a key function to list.sort to specify what to sort by.
my_list.sort(key=lambda x: (-x[1][1], -x[1][0]))
In general: to sort by multiple keys, if you have a stable sort (and Python's sort is stable), then you can do it in steps from the least important key to the primary key. In your case by the first number descending and then by the second number also descending:
s0 = [['A',(3,4)],['A2',(6,11)],['U1',(2,9)],['P9',(1,9)], ['X',(10,4)]]
s1 = sorted(s0, key = lambda x: x[1][0], reverse=True)
print(s1) # intermediate result
s2 = sorted(s1, key = lambda x: x[1][1], reverse=True)
print(s2) # final result

Python pick a random value from hashmap that has a list as value?

so I have a defaultdict(list) hashmap, potential_terms
potential_terms={9: ['leather'], 10: ['type', 'polyester'], 13:['hello','bye']}
What I want to output is the 2 values (words) with the lowest keys, so 'leather' is definitely the first output, but 'type' and 'polyester' both have k=10, when the key is the same, I want a random choice either 'type' or 'polyester'
What I did is:
out=[v for k,v in sorted(potential_terms.items(), key=lambda x:(x[0],random.choice(x[1])))][:2]
but when I print out I get :
[['leather'], ['type', 'polyester']]
My guess is ofcourse the 2nd part of the lambda function: random.choice(x[1]). Any ideas on how to make it work as expected by outputting either 'type' or 'polyester' ?
Thanks
EDIT: See Karl's answer and comment as to why this solution isn't correct for OP's problem.
I leave it here because it does demonstrate what OP originally got wrong.
key= doesn't transform the data itself, it only tells sorted how to sort,
you want to apply choice on v when selecting it for the comprehension, like so:
out=[random.choice(v) for k,v in sorted(potential_terms.items())[:2]]
(I also moved the [:2] inside, to shorten the list before the comprehension)
Output:
['leather', 'type']
OR
['leather', 'polyester']
You have (with some extra formatting to highlight the structure):
out = [
v
for k, v in sorted(
potential_terms.items(),
key=lambda x:(x[0], random.choice(x[1]))
)
][:2]
This means (reading from the inside out): sort the items according to the key, breaking ties using a random choice from the value list. Extract the values (which are lists) from those sorted items into a list (of lists). Finally, get the first two items of that list of lists.
This doesn't match the problem description, and is also somewhat nonsensical: since the keys are, well, keys, there cannot be duplicates, and thus there cannot be ties to break.
What we wanted: sort the items according to the key, then put all the contents of those individual lists next to each other to make a flattened list of strings, but randomizing the order within each sublist (i.e., shuffling those sublists). Then, get the first two items of that list of strings.
Thus, applying the technique from the link, and shuffling the sublists "inline" as they are discovered by the comprehension:
out = [
term
for k, v in sorted(
potential_terms.items(),
key = lambda x:x[0] # this is not actually necessary now,
# since the natural sort order of the items will work.
)
for term in random.sample(v, len(v))
][:2]
Please also see https://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/ to understand how the list flattening and result ordering works in a two-level comprehension like this.
Instead of the out, a simpler function, is:
d = list(p.values()) which stores all the values.
It will store the values as:
[['leather'], ['polyester', 'type'], ['hello', 'bye']]
You can access, leather as d[0] and the list, ['polyester', 'type'], as d[1]. Now we'll just use random.shuffle(d[1]), and use d[1][0].
Which would get us a random word, type or polyester.
Final code should be like this:
import random
potential_terms={9: ['leather'], 10: ['type', 'polyester'], 13:['hello','bye']}
d = list(p.values())
random.shuffle(d[1])
c = []
c.append(d[0][0])
c.append(d[1][0])
Which gives the desired output,
either ['leather', 'polyester'] or ['leather', 'type'].

Is it possible to sort a list of strings that represents Filipino numbers

list = [dalawa, tatlo, apat, siyam, isa] and is there a way to sort this to list = [isa, dalawa, tatlo, apat, siyam]. I an new in python so I don't have any idea about this.
The python sort() method will sort a list in alphabetical order.
What you can do is assign the value of each filipino number as a dictionary, and then sort it according to value.
That should be done as so: (I'm making up the values)
list = {"dalawa":2, "tatlo":3, "apat":4, "siyam":5, "isa":1}
# look up lambda functions in order to better understand the below functionality.
# In short what this does is, return to the sorted function the values of keys in the above dictionary and telling it to sort by them and not by the actual via resetting the key parameter to the lambda function.
result = sorted(list, key=lambda x:list[x[0]])

Sorting a dictionary by an inner "attribute" dictionary key-value

I created a dictionary using a for-loop and this code:
players[name] = {'roll_total': player_roll, 'ante': None}
Previously my dictionary was just players = {names: totals} and I could sort it using this code:
players = [(k, players[k]) for k in sorted(players, key=players.get, reverse=True)]
But now since I implemented the inner "attribute" dictionary, I get an error saying comparisons can't be made on dictionaries.
TypeError: '<' not supported between instances of 'dict' and 'dict'
So how can I modify the sorting method to compare values of the dictionaries (the roll_total values), and have my players dictionary sorted?
That happens because you are comparing two dictionaries.Use:
players = [(k, players[k]) for k in sorted(players, key=lambda x: players[x]['roll_total'], reverse=True)]
The lambda function receives the key of name as x and then sorts on the basis of players[x]['roll_total'] which is basically players[name]['roll_total'].
You can also make this a dictionary comprehension and then assign it to the variable name of the dictionary on which you're iterating. That way, you're retaining the dictionary and can change it at certain logical junctures, making it available in later program flow as sorted. For instance, maybe you want the winner to start the next round, or something like that, and instead of making a new dictionary from the list, you already have the dictionary at hand, in only one operation:
{k: players[k] for k in sorted(players, key=lambda x: players[x]['rolltotal'], reverse=True)}

How to order a list of tuples by the integer value of a certain index of each, in Python?

Given a list of tuples e.g.
[('a','b','4'),('c','d','9'),('e','f','2')]
The third element of each tuple will always be the string value of an integer.
I want to write each tuple as a row of a csv using csv.writerow().
Before I do, I want to reorder the tuples (ideally by overwriting the existing list or creating a new one) such that they get written in descending order of the integer value of that third element of each e.g.
c,d,9
a,b,4
e,f,2
I'm trying to imagine some sort of multiple if/else combo in a list comprehension, but surely there's go to be a simpler way?
The sorted function (or the list method sort) takes optional arguments reverse to allow you to sort in decreasing order, and key to allow you to specify what to sort by.
l = [('a','b','4'),('c','d','9'),('e','f','2')]
l.sort(key=lambda x: int(x[2]), reverse=True)
gives you the list in the order you want.
In my answer I use sys.stdout as an example but you may use a file instead
>>> import sys, csv
>>> items = [('a','b','4'),('c','d','9'),('e','f','2')]
>>> w = csv.writer(sys.stdout)
>>> w.writerows(sorted(items, key=lambda x: int(x[2]), reverse=True))
c,d,9
a,b,4
e,f,2
This works in both Python 2 and Python 3:
x = [('a','b','4'),('c','d','9'),('e','f','2')]
x.sort(key=lambda int(x:x[2]), reverse=True)
key is a function applied to each item in the list and returns the key to be used as the basis for sorting.
Another one using itemgetter slightly faster than lambda x: x[2] for small lists and considerably faster for larger lists.
from operator import itemgetter
l = [('a','b','4'),('c','d','9'),('e','f','2')]
l.sort(key=itemgetter(2), revese=True)
Python sorting mini Howto has got a lot many useful tricks worth reading.
Slightly different, albeit less efficient solution:
def sort_tuple_list(l):
sorted_ints = sorted([int(i[2]) for i in l], reverse=True)
sorted_tuple_list = []
for i in sorted_ints:
for tup in l:
if int(tup[2]) == i:
sorted_tuple_list.append(tup)
return sorted_tuple_list
Returns a list sorted according to the original question's specifications.
You can then simply write each row of this returned list to your csv file.

Categories