Execution order of nested dictionary comprehension [duplicate] - python

This question already has answers here:
How to construct nested dictionary comprehension in Python with correct ordering?
(2 answers)
Closed 2 years ago.
I'm trying to convert this list:
data = [{'A': 123}, {'B': 456}, {'C': 789}]
To this dictionary:
{'A': 123, 'B': 456, 'C': 789}
By using dictionary comprehension:
{key: value for key, value in dictionary.items() for dictionary in data}
Exception:
NameError: name 'dictionary' is not defined

You need to switch your iteration logic. Default order of iteration inside dictionary (or list) comprehension goes from left to right:
>>> data = [{'A': 123}, {'B': 456}, {'C': 789}]
>>> {k: v for d in data for k, v in d.items()}
{'A': 123, 'B': 456, 'C': 789}
You can create nested dictionary comprehension, list comprehension or generator expression using {}, [] and () respectively which are the exceptions for the left to right execution order I mentioned earlier. All these operations create new objects, and they execute inside their own scope.

Related

Checking if value is present in the list of key-value pairs, if not present then add the new key-pair value from iterables (not overwriting same key)

If I used the not in it still appends the new key-pair value even if a specific value is already in the list.
dict1 = {'a': 0, 'a': 5, 'b': 1, 'c': 2}
list1 = [{'a': 0}] # This key-pair value is already in the list1 but
# still added from dict1.
new1 = []
new2 = []
for key, value in dict1.items():
if value not in list1:
new1.append(key)
new2.append(value)
new0 = {new1[i]: new2[i] for i in range(len(new1))}
list1.append(new0)
Desired output is:
list1 = [{'a': 0, 'a': 5, 'b': 1, 'c': 2}]
(As I dont want to overwrite the key/s)
As you do not provide example data, I have to make some guesses here. If I guessed incorrectly, please provide the required information.
You call .items on list1. A list does not have a items function. Instead, I suspect your list1 is actually a dictionary, which would like this for example:
list1 = {
"a":1,
"b":2,
"c":3
}
In your current loop, you check if the value is within list2. If list2 is actually a list, you're doing so correctly. However, based on your title I assume what you actually want to do is check if the key is in list2, and if not add the key:value to list2. You could not add a key:value pair to a list, so I assume that list2 shoudl also be a dictionary. You would be able to add them add as a tuple, but based on the title I assume that is not what you want.
If you actually want to add it as a key:value pair to a dictionary, you could do that as follows:
list2 = {}
for key, value in list1.items():
if key not in list2.keys(): # check if this key already exists
list2[key] = value # if not, add they key with the value
As list1 and list2 are not actually instances of list, but of dict I would recommend renaming your variables to avoid future confusion. Hope that helps!
EDIT after update in question
Your example data had a small mistake, as there were two a keys, meaning that the first {'a':0} would be overwritten within dict1 already.
dict1 = {'a': 0, 'b': 5, 'c': 1, 'd': 2}
list1 = [{'a': 0}]
As I understand it, you wish to check if the value is already containted within a list of dictionaries.
As such, we need to get all the values from these dictionaries.
As you do not want to overwrite any keys, it would need to be a list of dictionaries that each only have one key. As such, we can get each individual dictionary, get the keys. This returns an dict_keys object, which we can convert to a list. Since each dictionary within list1 always only has one key, we can just take the first from said lsit.
[list(x.values())[0] for x in list1]
Putting that within the loop we get
for key, value in dict1.items():
if not value in [list(x.values())[0] for x in list1]:
# If no dictionary exists within `list1` with this value
list1.append({key:value}) # then add it as a new dictionary
This would return
[{'a': 0}, {'b': 5}, {'c': 1}, {'d': 2}]
You could run this code again with a different dict1 and it would not overwrite keys within list1, for example with:
dict1 = {'a': 9}
the output would become
[{'a': 0}, {'b': 5}, {'c': 1}, {'d': 2}, {'a': 9}]

Access nested dictionary without loops [duplicate]

This question already has answers here:
Access nested dictionary items via a list of keys?
(20 answers)
Closed 4 years ago.
If I have a nested dictionary
mydict={'a1':{'b1':1}, 'a2':2}
and a list of indexes index = ['a1', 'b1'] leading to an inner value, is there a pythonic / one-liner way to get that value, i.e. without resorting to a verbose loop like:
d = mydict
for idx in index:
d = d[idx]
print(d)
You can use functools.reduce.
>>> from functools import reduce
>>> mydict = {'a1':{'b1':1}, 'a2':2}
>>> keys = ['a1', 'b1']
>>> reduce(dict.get, keys, mydict)
1
dict.get is a function that takes two arguments, the dict and a key (and another optional argument not relevant here). mydict is used as the initial value.
In case you ever need the intermediary results, use itertools.accumulate.
>>> from itertools import accumulate
>>> list(accumulate([mydict] + keys, dict.get))
[{'a1': {'b1': 1}, 'a2': 2}, {'b1': 1}, 1]
Unfortunately, the function does not take an optional initializer argument, so we are prepending mydict to keys.

How to create a dictionary out of a list of lists in python?

Let's suppose I have the following list made out of lists
list1 = [['a','b'],['a'],['b','c'],['c','d'],['b'], ['a','d']]
I am wondering if there is a way to convert every element of list1 in a dictionary where all the new dictionaries will use the same key. E.g: if ['a']
gets to be {'a':1}, and ['b'] gets to be {'b':2}, I would like for all keys a the value of 1 and for all keys b the value of 2. Therefore, when creating the dictionary of ['a','b'], I would like to turn into {'a':1, 'b':2}.
What I have found so far are ways to create a dictionary out of lists of lists but using the first element as the key and the rest of the list as the value:
Please note that's not what I am interested in.
The result I would want to obtain from list1 is something like:
dict_list1 = [{'a':1,'b':2}, {'a':1}, {'b':2,'c':3}, {'c':3,'d':4}, {'b':2}, {'a':1,'d':4}]
I am not that interested in the items being that numbers but in the numbers being the same for each different key.
You need to declare your mapping first:
mapping = dict(a=1, b=2, c=3, d=4)
Then, you can just use dict comprehension:
[{e: mapping[e] for e in li} for li in list1]
# [{'a': 1, 'b': 2}, {'a': 1}, {'b': 2, 'c': 3}, {'c': 3, 'd': 4}, {'b': 2}, {'a': 1, 'd': 4}]
Using chain and OrderedDict you can do auto mapping
from itertools import chain
from collections import OrderedDict
list1 = [['a','b'],['a'],['b','c'],['c','d'],['b'], ['a','d']]
# do flat list for auto index
flat_list = list(chain(*list1))
# remove duplicates
flat_list = list(OrderedDict.fromkeys(flat_list))
mapping = {x:flat_list.index(x)+1 for x in set(flat_list)}
[{e: mapping[e] for e in li} for li in list1]
Here a try with ord() also it will work for both capital and lower letters :
[{e: ord(e)%32 for e in li} for li in list1]

Multi-Line Dictionary to Single-Line Dictionary [duplicate]

This question already has answers here:
How do I merge two dictionaries in a single expression in Python?
(43 answers)
Closed 5 years ago.
How would you go about converting a multi-line dictionary into one dictionary?
For example, the current dictionary, if printed to the screen, is of the form:
{'keyword':'value'}
{'keyword':'value'}
{'keyword':'value'}
{'keyword':'value'}
...and so on for over 100 hundred lines. How do you convert this to the following form:
{'keyword':'value','keyword':'value','keyword':'value','keyword':'value'}
Assuming you are asking for multiple dictionaries (not multiple line dictionary) to one dictionary.
a = {1: 1, 2:2}
b = {2:2, 3:3}
c = {2:3}
{**a, **b, **c}
Out: {1: 1, 2: 3, 3: 3}
Assuming that your initial data is actually a list of dictionaries and your keys are unique accross all your dictionaries I would use something like -
example = [{'a':1}, {'b':2}, {'c':3}]
objOut = {}
for d in example:
for k,v in d.iteritems():
objOut[k] = v
OR
objIn = [{'a':1}, {'b':2}, {'c':3}]
objOut = {}
for d in objIn:
objOut.update(d)
print objOut
Given
dicts = [{'a':1}, {'b':2}, {'c':3, 'd':4}]
Do
{k:v for d in dicts for (k, v) in d.items()}
or
from itertools import chain
dict(chain(*map(dict.items, dicts)))
resulting in
{'a': 1, 'b': 2, 'c': 3, 'd': 4}

Merge multiple list of dict with same value in common key [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I have 4 list of dicts
list1 = [{'a':0,'b':23}, {'a':3,'b':77},{'a':1,'b':99}]
list2 = [{'a':1,'c':666},{'a':4,'c':546}]
list3 = [{'d':33,'a':3},{'d':1111,'a':4},{'d':76,'a':1},{'d':775,'a':0}]
list4 = [{'a':2,'e':12},{'a':4,'e':76}]
Every dict in the list has a common key 'a'. My requirement is 'a' key with same value in dict from all the list should be merged and if a particular key does not exist in the dicts while merging, then assign 0 for those keys or just omit those keys.
for eg. for key 'a' with value 1, from above example we have 2 dicts, one from list1 i.e {'a':0,'b':23} and one is list3, last dict i.e {'d':775,'a':0}, so 1st we have identified the dicts with same 'a' value, now need to merge these dicts
i.e {'a':0, 'b':23, 'c':0, 'd':775, 'e':0}, since both the dict didn't have 'c', 'c' is assigned as 0 here
I should get the output as:
[{'a':0,'b':23,'c':0,'d':775, 'e':0},{'a':1,'b':99,'c':666,'d':76,'e':0},{'a':2,'b':0,'c':0,'d':0,'e':12},{'a':3,'b':77,'c':0,'d':33,'e':0}, {'a':4,'b':0,'c':546,'d':1111,'e':76}]
usings minimum loops or list comprehension
If you want a more pythonic way:
from itertools import groupby
from pprint import pprint
from collections import ChainMap
a = [{'a':0,'b':23}, {'a':3,'b':77}, {'a':1,'b':99}]
b = [{'a':1,'c':666}, {'a':4,'c':546}]
c = [{'d':33,'a':3}, {'d':1111,'a':4}, {'d':76,'a':1}, {'d':775,'a':0}]
d = [{'a':2,'e':12}, {'a':4,'e':76}]
dict_list = a + b + c + d
# You just need to specify the key you want to use in the lambda function
# There's no need to declare the different key values previously
res = map(lambda dict_tuple: dict(ChainMap(*dict_tuple[1])),
groupby(sorted(dict_list,
key=lambda sub_dict: sub_dict["a"]),
key=lambda sub_dict: sub_dict["a"]))
pprint(list(res))
Outputs:
[{'a': 0, 'b': 23, 'd': 775},
{'a': 1, 'b': 99, 'c': 666, 'd': 76},
{'a': 2, 'e': 12},
{'a': 3, 'b': 77, 'd': 33},
{'a': 4, 'c': 546, 'd': 1111, 'e': 76}]
Edit (Improvement):
You can also use
from _operator import itemgetter
key=itemgetter("a")
instead of
key=lambda sub_dict: sub_dict["a"]
The version with itemgetter is much faster. Using the example you provided:
- Lambda: 0.037109375ms
- Itemgetter: 0.009033203125ms

Categories