simplify code using an inverse dictionary in python - python

Consider some mapping my_map that defines the order of some keys, and some dictionary my_dict that maps the same keys into some values:
my_map = {'x' : 2, 'y' : 0, 'z' : 1}
my_dict = {'x' : 'foo', 'z' : 'bar', 'y' : 'baz'}
I want to get an ordered list of the values of my_dict using the order defined by my_map. My best approach of getting there is:
inv_map = {v: k for k, v in my_map.items()}
ordered_list = [my_dict[k] for k in [inv_map[d] for d in range(len(my_map))]]
Is there a less clunky way of doing the same?

You could use the sorted function to order your map by value and then convert it:
[my_dict[k] for k in sorted(my_map, key=lambda key: my_map[key])]
Somewhat cleaner at least!
Let's make sure that it works:
>>> my_map = {'x' : 2, 'y' : 0, 'z' : 1}
>>> my_dict = {'x' : 'foo', 'z' : 'bar', 'y' : 'baz'}
>>> [my_dict[k] for k in sorted(my_map, key=lambda key: my_map[key])]
['baz', 'bar', 'foo']

You can actually use sorted very efficiently here using dict.get:
[my_dict[k] for k in sorted(my_map, key=my_map.get)]
In action:
>>> my_map = {'x' : 2, 'y' : 0, 'z' : 1}
>>> my_dict = {'x' : 'foo', 'z' : 'bar', 'y' : 'baz'}
>>> [my_dict[k] for k in sorted(my_map, key=my_map.get)]
['baz', 'bar', 'foo']

Depends on the situation you can init final list and pass items to the needed positions
result = [None] * len(my_dict)
for k, v in my_dict.items():
result[my_map[k]] = v

Still another variation (I think it's different from already presented solutions):
[x[1] for x in sorted(my_dict.items(), key=lambda elem: my_map[elem[0]])]
Testing code:
my_map = {'x' : 2, 'y' : 0, 'z' : 1}
my_dict = {'x' : 'foo', 'z' : 'bar', 'y' : 'baz'}
print(my_dict.items())
sorted_result=[x[1] for x in sorted(my_dict.items(), key=lambda elem: my_map[elem[0]])]
print(sorted_result)
Or a bit differently:
sorted_result=list(zip(*sorted(my_dict.items(), key=lambda elem: my_map[elem[0]])))[1]
I wanted to use zip() to split a list of tuples into 2 lists, but in Python 3 zip() returns iterator (not a list), so (as suggested in Transpose/Unzip Function (inverse of zip)?) I wrapped it in list()

you can use sorted with the values from my_dict and a key function that sorts them with the values from my_map
ordered_list = sorted(my_dict.values(), key=lambda s:my_map[{v: k for k, v in my_dict.items()}[s]])
if it's sorted the wrong way you can use reverse=True

Related

How to split a dictionary into single-key dictionaries? [duplicate]

This question already has answers here:
Iterating over dictionaries using 'for' loops
(15 answers)
Closed 12 months ago.
I have a dict with n keys.
The problem is I don't want a single dictionary, but a list of dictionaries per single key-value pair.
For example:
d = {'a' : -15, 'b' : -71.8333, 'c' : 'a'}
The output I want:
[{'a' : -15},
{'b' : -71.8333},
{'c' : 'a'}]
You can iterate over the dict's key-value pairs using dict.items and make a new dict from each one:
d = {'a' : -15, 'b' : -71.8333, 'c' : 'a'}
dicts = []
for key, value in d.items():
dicts.append({key: value}
Or as a list-comprehension:
dicts = [{key: value} for key, value in d.items()]
Try this:
d = {'a' : -15, 'b' : -71.8333, 'c' : 'a'}
for i, o in d.items():
print({i: o})
Will give:
{'a': -15}
{'b': -71.8333}
{'c': 'a'}

correcting unhashable type: 'dict_keys'

try to find the max value in a nested dictionary, but showed unhashable type: 'dict_keys'error
Suppose I have this dictionary:
d = {'A': {'a':2, 'b':2, 'c':0},
'B': {'a':2, 'b':0, 'c':1}}
I want the code to return all the key(s) that contain maximum values within the dictionary (i.e. the maximum value in dictionary A is 2, and I want the code to return me the corresponding keys: 'a' and 'b')
['a','b']
here is the code I wrote:
max_value = max(d[Capital_Alph].values()))
return [key for key, value in d[Capital_Alph].items()
if value == max_value]
So you have a dictionary with a str as value and a dict as key, You can do something like this:
d = {'A': {'a':2, 'b':2, 'c':0},
'B': {'a':2, 'b':0, 'c':1}}
print(list(d['A'].keys()))
Returns:
['a', 'b', 'c']
[Finished in 0.8s]
Is this a viable solution to what you are trying to accomplish?
You can not use non-hashable datatypes as keys for sets or dict. You can accomplish your task by:
d = {'A': {'a':2, 'b':2, 'c':0},
'B': {'a':2, 'b':0, 'c':1}}
max_v = {k:max(d[k].values()) for k in d } # get the max value of the inner dict
print(max_v)
for inner in max_v:
print("Max keys of dict {} are: {}".format(inner,
[k for k,v in d[inner].items() if v == max_v[inner]]))
Output:
{'A': 2, 'B': 2} # max values of inner dicts
Max keys of dict A are: ['a', 'b']
Max keys of dict B are: ['a']
The part [k for k,v in d[inner].items() if v == max_v[inner]])) is needed to get all inner keys (if multiple exists) that have the same maximum value.
There are two errors in your code: there are too many ) characters in your calculation of max_value and you can't use return outside a function.
But if I fix those issues and do this:
>>> d = {'A': {'a':2, 'b':2, 'c':0},
'B': {'a':2, 'b':0, 'c':1}}
>>> Capital_Alph = "A"
>>> max_value = max(d[Capital_Alph].values())
>>> [key for key, value in d[Capital_Alph].items()
if value == max_value]
['a', 'b']
it's clear that there isn't a lot else wrong here. To avoid complicating things I didn't put the obvious loop around this:
for Capital_Alph in d:
but you can manage that on your own. Your error message is because you tried to make Capital_Alph a dict_keys object, in other words d.keys(), and use that as a key. You can't do that. You have to step through the list of dictionary keys yourself.

Print Value of Key in Lists of Dicts in Python

I am trying to print the value of a specific key in a list of dicts:
eg:
list = [{'a' : 123, 'b': 'xyz', 'c': [1,2]}, {'a' : 456, 'b': 'cde', 'c': [3,4]}]
I was hoping to be able to print the following for each dict:
print ("a: ", a)
print ("b: ", b)
If you're guaranteed those keys exist, a nice solution using operator.itemgetter:
from operator import itemgetter
# Renamed your list; don't name variables list
for a, b in map(itemgetter('a', 'b'), mylist):
print("a:", a)
print("b:", b)
The above is just a slightly optimized version of the import free code, pushing the work of fetching values to the builtins instead of doing it over and over yourself.
for d in mylist: # Renamed your list; don't name variables list
print("a:", d['a'])
print("b:", d['b'])
Oh, and for completeness (Aaron Hall is right that it's nice to avoid redundant code), a tweak to itemgetter usage to observe DRY rules:
keys = ('a', 'b')
for values in map(itemgetter(*keys), mylist):
for k, v in zip(keys, values):
print(k, v, sep=": ")
How about some nested loops, to avoid hard-coding it?
for dictionary in list: # rename list so you don't overshadow the builtin list
for key in ('a', 'b'):
print(key + ':', dictionary[key])
which should output:
a: 123
b: xyz
a: 456
b: cde
lst = [{'a' : 123, 'b': 'xyz', 'c': [1,2]}, {'a' : 456, 'b': 'cde', 'c': [3,4]}]
output=['a','b']
for dct in lst:
for k in output:
print(k+': '+str(dct[k]))

Duplicate values in a Python dictionary

I have a dictionary in the following format:
{ 'a' : [1], 'b' : [1,2,3], 'c' : [1,1,2], 'd' : [2,3,4] }
and I want to create a list of the keys which have a '1' in their values.
So my result list should look like:
['a','b','c','c']
I cannot understand how to work with duplicate values.
Any ideas how can I get such a list?
You can use list comprehensions
>>> d = { 'a' : [1], 'b' : [1,2,3], 'c' : [1,1,2], 'd' : [2,3,4] }
>>> [key for key, values in d.items() for element in values if element==1]
['c', 'c', 'b', 'a']
Here we have two nested for loops in our list comprehension. The first iterate over each key, values pairs in the dictionary and the second loop iterate over each element in the "value" list and return the key each time that element equal to 1. The result list is unordered because dict are unordered which means there are no guarantees about the order of the items.
Here is one way:
>>> x = { 'a' : [1], 'b' : [1,2,3], 'c' : [1,1,2], 'd' : [2,3,4] }
>>> list(itertools.chain.from_iterable([k]*v.count(1) for k, v in x.iteritems() if 1 in v))
['a', 'c', 'c', 'b']
If using Python 3, use items instead of iteritems.
This uses two loops, k,v in d.items() which gets each (key,value) pair from the dictionary, and n in v which loops through each value in v:
d = { 'a' : [1], 'b' : [1,2,3], 'c' : [1,1,2], 'd' : [2,3,4] }
l = []
for k,v in d.items():
for n in v:
if n == 1:
l.append(k)
l.sort()
If you want a one-liner:
l = sorted(k for k,v in d.items() for n in v if n == 1)
The sort must be made on the dictionary to get the expected result. This should work:
list = []
for i in sorted(d.keys()):
list+=[i for x in d[i] if x == 1]
print list
will output:
['a', 'b', 'c', 'c']
easy way: (Python 3)
d = { 'a' : [1], 'b' : [1,2,3], 'c' : [1,1,2], 'd' : [2,3,4] }
n = 1
result = []
for key, value in d.items():
for i in value.count(n):
res.append(key)
if you want list sorted than:
result.sort()

Sorting alphanumeric keys of a dictionary in python

I have a python dictionary whose keys have the following pattern
<some x number of digits/alphabets> <some y number of alphabets><some z number of digits>
I want to sort the dictionary based on this keys.
For e.g
01IB0610, 01IB062, 01IB064
should be 01IB062, 01IB064 01IB0610
Complete example is something like this:
{ '01IB0610' : {'a' : [] , 'b': [] }, '01IB062' : {'a' : [] , 'b': [] } , '01IB064' : {'a' : [] , 'b': [] }
Final Output should be:{ '01IB062' : {'a' : [] , 'b': [] }, '01IB064' : {'a' : [] , 'b': [] } , '01IB0610' : {'a' : [] , 'b': [] }
import re
def key_func(s):
return [int(x) if x.isdigit() else x for x in re.findall(r'\D+|\d+', s)]
sorted_keys = sorted(d, key=key_func)
Example:
>>> d = {'01IB0610': 'foo', '01IB062': 'bar', '01IB0604': 'baz'}
>>> sorted(d, key=key_func)
['01IB062', '01IB0604', '01IB0610']
I'm not sure I totally get the sorting criteria, but you can use an OrderedDict to have a dict that keeps particular order.
from collections import OrderedDict
import re
d = {'01IB0610': 1, '01IB062': 2, '01IB064': 3}
def criteria(x):
number = int(re.sub('[^0-9]', '', x[0]) )
length = len(x[0])
return length, number
d = OrderedDict( sorted( d.items(), key = criteria ) )
d.keys()
>> ['01IB062', '01IB064', '01IB0610']
This creates an OrderedDict where the order of the elements in the original dict is based on a hierarchical sort of the keys of those elements. The first criteria is the length of the key, ie 01IB0610 comes after 01IB064 because its longer. The second criteria is based on the digits in the key, ie 01062 is before 01064.

Categories