Merge two dictionaries of dictionaries
My question is similar to this one, but the answers don't produce the right result (for me?).
Take these dictionaries:
a = {'a': {'a': 1}}
b = {'a': {'b': 2}}
I want to produce:
c = {'a': {'a': 1, 'b': 2}}
Using the answers from the quoted question, these all produce:
c = a.copy()
c.update(b)
>>
c == {'a': {'b': 2}
Consider that a and b might be more complex than this, for example:
a = {'a': {'aa': {'aaa': 1}, 'bb': {'bbb': 2}}}
b = {'a': {'bb': {'aaa': 1}, 'bb': {'bbb': 2}}}
In this case you can use
>>> a['a'].update(b['a'])
>>> a
{'a': {'a': 1, 'b': 2}}
Element in dictionary is also dictionary, so you can treat that element as dictionary.
As for more complex example I don't know what result should be. But in general, you can access elements in element as dictionary in nested for loops.
Related
I have the following list of dicts:
lst = [{'a':1, 'b':2, 'c':3}, {'a':1, 'b':2, 'd':3}, {'a':1, 'c':2, 'k':3}, {'d':1, 'k':2, 'l':3}]
I want to filter the list of dicts (in my case it's a list of thousands or even more dicts, with different keys with some overlap) to be a list containing all the dicts that have keys: ["a", "b"]. I want to filter each dict only to these a and b keys, and if they don't exist, don't include the dictionary in the final list. I am using:
[{"a": d.get("a"), "b": d.get("b")} for d in lst]
Please advise for an elegant way to solve it.
The dictionary keys-view is set-like, so it supports subset comparisons by using <= operator:
>>> keys = set("ab")
>>> [{k: d[k] for k in keys} for d in lst if keys <= d.keys()]
[{'a': 1, 'b': 2}, {'a': 1, 'b': 2}]
I have figured it out and here is my alternative:
lst = [{'a':1, 'b':2, 'c':3}, {'a':1, 'b':2, 'd':3}, {'a':1, 'c':2, 'k':3}, {'d':1, 'k':2, 'l':3}]
keys = set("ab")
[i for i in [{k: d.get(k) for k in keys if k in d} for d in lst] if i]
Gives the desired answer:
[{'b': 2, 'a': 1}, {'b': 2, 'a': 1}, {'a': 1}]
sem1_credit = {'A': 4, 'B': 4, 'C': 3}
sem2_credit = {'D': 5, 'E': 1}
sem3_credit = {'F': 3}
e = 2
for j in range(e):
for i in 'sem'+str(j+1)+'_credit':
I wanted to use loop to access different dict. So I tried to create the dict name with concatenation using loop. But it doesn't work. Is there a way to work it out or is there some other way to approach dict without loops.
You can get a dictionary of the current local symbols table by calling locals(). So locals()['sem1_credit'] is essentially this sem1_credit.
From here, you can build a loop:
sem1_credit = {'A': 4, 'B': 4, 'C': 3}
sem2_credit = {'D': 5, 'E': 1}
sem3_credit = {'F': 3}
for idx in range(1, 4):
credits = locals()[f'sem{idx}_credit']
for key, credit in credits.items():
print(f"{key} {credit}")
Keep in mind that the range(num) generate numbers from 0 to num-1. So in your code, range(2) only generates 0 and 1.
I have a list of the dictionary as follows:
[{"A":5,"B":10},
{"A":6,"B":13},
{"A":10,"B":5}]
I want to this list in decending order on the value of B. The output should look like this:
[{"A":6,"B":13},
{"A":5,"B":10},
{"A":10,"B":5}]
How to do that?
You can sort lists by the results of applying a function to each element: https://docs.python.org/3.9/library/functions.html#sorted
>>> data = [{"A":5,"B":10},
... {"A":6,"B":13},
... {"A":10,"B":5}]
>>> sorted(data, key=lambda dct: dct["B"], reverse=True)
[{'A': 6, 'B': 13}, {'A': 5, 'B': 10}, {'A': 10, 'B': 5}]
I have the following for instance:
x = [{'A':1},{'A':1},{'A':2},{'B':1},{'B':1},{'B':2},{'B':3},{'C':1},{'D':1}]
and I would like to get a dictionary like this:
x = [{'A': [1,2], 'B': [1,2,3], 'C':[1], 'D': [1]}]
Do you have any idea how I could get this please?
You could use a collections.defaultdict of sets to collect unique values, then convert the final result to a dictionary with values as lists using a dict comprehension:
from collections import defaultdict
lst = [{'A':1},{'A':1},{'A':2},{'B':1},{'B':1},{'B':2},{'B':3},{'C':1},{'D':1}]
result = defaultdict(set)
for dic in lst:
for key, value in dic.items():
result[key].add(value)
print({key: list(value) for key, value in result.items()})
Output:
{'A': [1, 2], 'B': [1, 2, 3], 'C': [1], 'D': [1]}
Although its probably better to add your data directly to the defaultdict to begin with, instead of creating a list of singleton dictionaries(don't recommend this data structure) then converting the result.
Using dict.setdefault
Ex:
x = [{'A':1},{'A':1},{'A':2},{'B':1},{'B':1},{'B':2},{'B':3},{'C':1},{'D':1}]
res = {}
for i in x:
for k, v in i.items():
res.setdefault(k, set()).add(v)
#or res = [{k: list(v) for k, v in res.items()}]
print(res)
Output:
{'A': {1, 2}, 'B': {1, 2, 3}, 'C': {1}, 'D': {1}}
im trying to build a nested dictionary based on 3 pandas df columns:
dataframe: stops
columns: 'direction' (1-2) ,'stop_num'(1-23 if the direction is 1 and 100-2300 if direction is 2),'name_eng'
what i was trying to do is:
dct = {x: {y:z} for x, y, z in zip(stops['direction'],stops['name_eng'],stops['stop_num'])}
the result i get is a nested dictionary indeed but for unknown reason i get only the last value in y:z so the dictionary look like:
{1:{1:'aaa'},'2:{100:'bbb'}}
any idea what am i doing wrong?
what i need is a nested dictionary with two dictionaries for each direction.
thanks!
Imagine your columns are:
1 a 1a
1 b 1b
2 a 2a
2 b 2b
Now, try your code:
>>> {x: {y:z} for x, y, z in zip([1,1,2,2], ['a', 'b', 'a', 'b'], ['1a', '1b', '2a', '2b'])}
{1: {'b': '1b'}, 2: {'b': '2b'}}
You have a loop over the tuples: (1, 'a', '1a'), (1, 'b', '1b'), (2, 'a', '2a'), (2, 'b', '2b').
The first element of the tuple is the "main" key of your dictionary. Thus, the dict is {1: {'a':'1a'}} after the first tuple.
Then comes (1, 'b', '1b'). The value of the main key 1 is overwritten and the dict becomes: {1: {'b':'1b'}}.
The next steps are: {1: {'b':'1b'}, 2: {'a': '2a'}} and {1: {'b': '1b'}, 2: {'b': '2b'}}
To avoid the overwrite, you can do:
>>> d = {}
>>> for x, y, z in zip([1,1,2,2], ['a', 'b', 'a', 'b'], ['1a', '1b', '2a', '2b']):
... d.setdefault(x, {}).update({y:z})
...
>>> d
{1: {'a': '1a', 'b': '1b'}, 2: {'a': '2a', 'b': '2b'}}
The idea is to create a new dict for every new main key (setdefault(..., {})) and to update the dict associated with the main key (update({y:z})).
If you want a dict comprehension, this one will work:
>>> {x: {y:z for k, y, z in zip([1,1,2,2], ['a', 'b', 'a', 'b'], ['1a', '1b', '2a', '2b']) if k==x} for x in set([1,1,2,2])}
{1: {'a': '1a', 'b': '1b'}, 2: {'a': '2a', 'b': '2b'}}
But it's far less efficient than the for loop because you loop once over the first column to get the main keys, then once again over all the rows, for every main key.