Pythonic way of switching nested dictionary keys with nested values - python

In short I'm working with a nested dictionary structured like this:
nested_dict = {'key1':{'nestedkey1': 'nestedvalue1'}}
I'm trying to find a pythonic way of switching the keys with the nested values, so it would look like this:
nested_dict = {'nestedvalue1':{'nestedkey1': 'key1'}}
I'm also trying to rename the nested key values, so ultimately the dictionary would look like this:
nested_dict = {'nestedvalue1':{'NEWnestedkey1': 'key1'}}
This is closer to what I'm working with:
original_dict = {
'buford': {'id': 1},
'henley': {'id': 2},
'emi': {'id': 3},
'bronc': {'id': 4}
}
I want it to look like this:
new_dict = {
1: {'pet': 'buford'},
2: {'pet': 'henley'},
3: {'pet': 'emi'},
4: {'pet': 'bronc'}
}
Is there a way to do this in one line using a dictionary comprehension? I'm trying to get the very basics here and avoid having to use things like itertools.

You can use a dictionary comprehension to achieve this, 'swapping' things round as you build it:
new_dict = {v['id']: {'pet': k} for k, v in original_dict.items()}
To expand it to a for loop, it'd look something like:
new_dict = {}
for k, v in original_dict.items():
new_dict[v['id']] = {'pet': k}
Note that both cases obviously rely on the 'id' value being unique, or the key will be overwritten for each occurrence.

For a more generic solution, you can try this:
def replace(d, change_to = 'pet'):
return {b.values()[0]:{change_to:a} for a, b in d.items()}
Output:
{1: {'pet': 'buford'}, 2: {'pet': 'henley'}, 3: {'pet': 'emi'}, 4: {'pet': 'bronc'}}

Related

Flatten list of dictionaries with multiple key, value pairs

I have a list of dictionaries with multiple KVP each
list_dict = [{'id': 1, 'name': 'sana'}, {'id': 2, 'name': 'art'}, {'id': 3, 'name': 'tiara'}]
I want to transform this into this format:
final_dict = {1: 'sana', 2: 'art', 3: 'tiara'}
I've been trying dict comprehensions but it does not work. Here's the best that I could do:
{k:v for d in list_dict for k, v in d.items()}
You don't need d.items(), you can just access the id and name properties of each dict.
{d['id']: d['name'] for d in list_dict}
for each element of the list you want the d["id"] to be the key and d["name"] to be the value, so the dictionary comprehension would look like this:
{d["id"]: d["name"] for d in list_dict}
You can try
final_dict={}
for dico in list_dict:
final_dict[dico['id']] = dico['name']
There's probably a few different ways you can do this. Here's a nice simple way of doing it using a pandas dataframe:
import pandas as pd
df = pd.DataFrame(list_dict) # <- convert to dataframe
df = df.set_index('id') # <- set the index to the field you want as the key
final_dict = df['name'].to_dict() # <- convert the series 'name' to a dict
print(final_dict)
{1: 'sana', 2: 'art', 3: 'tiara'}

How to group Python dictionary values which are dictionaries themselves

I'd like to group by the values of the following dictionary:
my_dict = {"Q1": {0: "no", 1: "yes"}, "Q2": {0: "no", 1: "yes"},
"Q3": {1: "animal", 2: "vehicle"}, Q4: {1: "animal", 2: "vehicle"}}
The result should look like this:
result = {("Q1", "Q2"): {0: "no", 1: "yes"},
("Q3", "Q4"): {1: "animal", 2: "vehicle"}}
I've tried the solutions listed here:
Grouping Python dictionary keys as a list and create a new dictionary with this list as a value
Using collections.defaultdict does not work because the result would imply that the dictionaries which I use as a key for grouping end up as keys of the result dictionary like that:
result = {{0: "no", 1: "yes"}: ["Q1", "Q2"] ,
{1: "animal", 2: "vehicle"}: ["Q3", "Q4"]}
Of course this does not work because keys of dictionaries have to be immutible. So I would require something like a frozendict which is not available in the standard library of python.
Using itertools.groupby also does not work because it requires the data to be sorted. But operator.itemgetter cannot sort dictionaries. It says:
TypeError: '<' not supported between instances of 'dict' and 'dict'
Therefore, I'd like to know a Pythonic way of solving this problem! Thank you for your help :)
Instead of using frozendict, you can use frozenset's of the dictionaries' items:
intermediate_dict = defaultdict(list)
for k, v in my_dict.items():
intermediate_dict[frozenset(v.items())].append(k)
result = {tuple(v): dict(k) for k, v in intermediate_dict.items()}
Output:
{('Q1', 'Q2'): {0: 'no', 1: 'yes'}, ('Q3', 'Q4'): {1: 'animal', 2: 'vehicle'}}
The above does not assume or require sorted input, making it O(n) for all cases, while sorting is O(n log n).
Assuming a sorted dictionary by value, you can use itertools.groupby:
{tuple(g): k for k, g in groupby(my_dict, key=my_dict.get)}
Code:
from itertools import groupby
my_dict = {"Q1": {0: "no", 1: "yes"}, "Q2": {0: "no", 1: "yes"},
"Q3": {1: "animal", 2: "vehicle"}, "Q4": {1: "animal", 2: "vehicle"}}
print({tuple(g): k for k, g in groupby(my_dict, key=my_dict.get)})
# {('Q1', 'Q2'): {0: 'no', 1: 'yes'}, ('Q3', 'Q4'): {1: 'animal', 2: 'vehicle'}}
So I would require something like a frozendict which is not available in the standard library of python.
Could you elaborate on this? While frozendict is not in the language standard, there's an extension available that you could install: https://pypi.org/project/frozendict/
Alternatively, you can turn the dictionaries into a tuple of (key-sorted) (key, value) items to get an immutable, canonical and reversible representation that can be used as a dictionary key.
(Note that if the dictionaries can have further mutable values inside them, you might need to do this recursively.)
Edit: Or use a frozenset() for the items, as the other answer points out. Note that this also requires recursively ensuring the values of the inner dictionary are immutable.
Here is another way using both frozenset and groupby
from operator import itemgetter
from itertools import groupby
first = itemgetter(0)
second = itemgetter(1)
my_hashes = sorted([(k, hash(frozenset(v))) for k, v in my_dict.items()], key=second)
d = dict()
for k, v in groupby(my_hashes, key=second):
items = list(v)
d[tuple(map(first, items))] = my_dict.get(first(first(items)))
print(d)
{('Q3', 'Q4'): {1: 'animal', 2: 'vehicle'}, ('Q1', 'Q2'): {0: 'no', 1: 'yes'}}

Merging n'th level multidimensional dictionary

Ok so what I have is:
foo = {1: {'f1': 'c1'}, 2: {'f2': 'c2'}}
What I want to get using list comprehension(if possible of course) is merge all 2nd level dictionary values this:
bar = {'f1': 'c1', 'f2': 'c2'}
What I tried is this:
bar = {k:v for k, v in tmp.items() for tmp in list(foo.values())}
Now this gets me:
NameError: name 'tmp' is not defined
This seamed like logical solution but apparently its a no go, could you please suggest a solution using list comprehension if possible, or some other one liner, or if that would not be possible an insight into why I get this error will be good knowledge to have.
Thanks,
You can use a dictionary comprehension:
foo = {1: {'f1': 'c1'}, 2: {'f2': 'c2'}}
bar = {k: v for d in foo.values() for k, v in d.items()}
{'f1': 'c1', 'f2': 'c2'}
You were close, but the order of your nesting is incorrect. This is tricky and not intuitive to everyone. Read your comprehension as you would write a normal for loop.
There is also no need to make a list out of your dictionary values.
Related: Understanding nested list comprehension.

How to list all the keys with the same value in a dictionary? [duplicate]

I have question about Dictionaries in Python.
here it is:
I have a dict like dict = { 'abc':'a', 'cdf':'b', 'gh':'a', 'fh':'g', 'hfz':'g' }
Now i want to get all Key-Elements by the same value and save it in a new dict.
The new Dict should be look like:
new_dict = { 'b':('cdf'), 'a':('abc','gh'), 'g':('fh','hfz')}
If you are fine with lists instead of tuples in the new dictionary, you can use
from collections import defaultdict
some_dict = { 'abc':'a', 'cdf':'b', 'gh':'a', 'fh':'g', 'hfz':'g' }
new_dict = defaultdict(list)
for k, v in some_dict.iteritems():
new_dict[v].append(k)
If you want to avoid the use of defaultdict, you could also do
new_dict = {}
for k, v in some_dict.iteritems():
new_dict.setdefault(v, []).append(k)
Here's a naive implementation. Someone with better Python skills can probably make it more concise and awesome.
dict = { 'abc':'a', 'cdf':'b', 'gh':'a', 'fh':'g', 'hfz':'g' }
new_dict = {}
for pair in dict.items():
if pair[1] not in new_dict.keys():
new_dict[pair[1]] = []
new_dict[pair[1]].append(pair[0])
print new_dict
This produces
{'a': ['abc', 'gh'], 'b': ['cdf'], 'g': ['fh', 'hfz']}
If you do specifically want tuples as the values in your new dictionary, you can still use defaultdict, and use tuple concatenation. This solution works in Python 3.4+:
from collections import defaultdict
source = {'abc': 'a', 'cdf': 'b', 'gh': 'a', 'fh': 'g', 'hfz': 'g'}
target = defaultdict(tuple)
for key in source:
target[source[key]] += (key, )
print(target)
Which will produce
defaultdict(<class 'tuple'>, {'a': ('abc', 'gh'), 'g': ('fh', 'hfz'), 'b': ('cdf',)})
This will probably be slower than generating a dictionary by list insertion, and will create more objects to be collected. So, you can build your dictionary out of lists, and then map it into tuples:
target2 = defaultdict(list)
for key in source:
target2[source[key]].append(key)
for key in target2:
target2[key] = tuple(target2[key])
print(target2)
Which will give the same result as above.
It can be done this way too, without using any extra functions .
some_dict = { 'abc':'a', 'cdf':'b', 'gh':'a', 'fh':'g', 'hfz':'g' }
new_dict = { }
for keys in some_dict:
new_dict[some_dict[keys]] = [ ]
for keys in some_dict:
new_dict[some_dict[keys]].append(keys)
print(new_dict)

Python convert sorted List to Dict with position specified as (key, value) pair

Just new to Python, what's the best way to convert a sorted list (yes, elements in list are unique):
[a0, a1, a2, a3, ..., aj, ... ]
to a Dict data type where position looks like following:
{
a0: {'index':0},
a1: {'index':1},
a2: {'index':2},
...
aj: {'index':j},
...
}
Just clarify some concerns here:
There are actually more pair in the dict such as {'index': j, 'name': wow, ...} and convert from list to such dict is necessary, the other properties, such as 'name' will be added after the dict been create, so basically it looks like following, first create the dict, and second based on the key aj adding other properties, other properties come later;
Explicitly define the index is necessary, it finally will looks like: {'index': myFunc(j)}.
Deeply appreciate your help!
What I have tried:
tried l = zip(mylist, range(len(mylist))) and convert l (which looks like [(a0, 0), (a1, 1), ...]) to dict, however, it's list with tuple inside;
tried d = dict(zip(mylist, range(mylist.len))) but still need convert {ai: i} into {ai:{'index': i}} and don't know the brilliant method to solve from here;
tried naive for loop which works, but not happen with that
Using Dict comprehension (one-liner; if you didn't have the two concerns):
result = {key: {"index": index} for index, key in enumerate(yourList)}
You can use it like:
>>> yourList = range(10)
>>> result = {key: {"index": index} for index, key in enumerate(yourList)}
>>> result
{0: {'index': 0}, 1: {'index': 1}, 2: {'index': 2}, 3: {'index': 3}, 4: {'index': 4}, 5: {'index': 5}, 6: {'index': 6}, 7: {'index': 7}, 8: {'index': 8}, 9: {'index': 9}}
For a solution that accounts for those two bullets, I'd suggest something like the below:
result = {}
for index, item in enumerate(yourList):
currentDict = {"name": "wow", .. all your other properties .. }
currentDict["index"] = index #Or may be myFunc(index)
result[item] = currentDict
NOTE: I hope you are using hashable items in your original list.

Categories