trying to figure out how I might be able to use list comprehension for the following:
I have a dictionary:
dict = {}
dict ['one'] = {"tag":"A"}
dict ['two'] = {"tag":"B"}
dict ['three'] = {"tag":"C"}
and I would like to create a list (let's call it "list") which is populated by each of the "tag" values of each key, i.e.
['A', 'B', 'C']
is there an efficient way to do this using list comprehension? i was thinking something like:
list = [x for x in dict[x]["tag"]]
but obviously this doesn't quite work. any help appreciated!
This is an extra step but gets the desired output and avoids using reserved words:
d = {}
d['one'] = {"tag":"A"}
d['two'] = {"tag":"B"}
d['three'] = {"tag":"C"}
new_list = []
for k in ('one', 'two', 'three'):
new_list += [x for x in d[k]["tag"]]
print(new_list)
Try this:
d = {'one': {'tag': 'A'},
'two': {'tag': 'B'},
'three': {'tag': 'C'}}
tag_values = [d[i][j] for i in d for j in d[i]]
>>> print tag_values
['C', 'B', 'A']
You can sort the list afterwards if it matters.
If you have other key/value pairs in the inner dicts, apart from 'tag', you may want to specify the 'tag' keys, like this:
tag_value = [d[i]['tag'] for i in d if 'tag' in d[i]]
for the same result. If 'tag' is definitely always there, remove the if 'tag' in d[i] part.
As a side note, never a good idea to call a list 'list', since it's a reserved word in Python.
You can try this:
[i['tag'] for i in dict.values()]
I would do something like this:
untransformed = {
'one': {'tag': 'A'},
'two': {'tag': 'B'},
'three': {'tag': 'C'},
'four': 'bad'
}
transformed = [value.get('tag') for key,value in untransformed.items() if isinstance(value, dict) and 'tag' in value]
It also sounds like you're trying to get some info out of JSON you might want to look into a tool like https://stedolan.github.io/jq/manual/
Related
I have a list of lists and I want to get a dictionary of dictionaries:
import json
list = [
['1', '2', '3'],
['a', 'b'],
['I', 'II'],
['A', 'B', 'C'],
['A', 'B', 'D']
]
dict = {}
for val in list:
count = len(val)
if val[0] not in dict:
dict[val[0]] = {}
if count == 3:
if val[1] not in dict[val[0]]:
dict[val[0]][val[1]] = {}
if val[2] not in dict[val[0]][val[1]]:
dict[val[0]][val[1]][val[2]] = ''
else:
if val[1] not in dict[val[0]]:
dict[val[0]][val[1]] = ''
print (json.dumps(dict, sort_keys=True, indent=4))
output:
{
"1": {
"2": {
"3": ""
}
},
"A": {
"B": {
"C": "",
"D": ""
}
},
"I": {
"II": ""
},
"a": {
"b": ""
}
}
So it works with 2 or 3 elements in lists, but if I have more (random) elements of lists, I have to have kind of recursive function, that I can't think of.
There is no real need for a recursive function here (unless it's a requirement). You also don't need to care for the size or amount of lists. Simply iterate through each list while keeping an updated reference for the inner dicts as you go.
You can also use setdefault to avoid the checks if a key exists already.
d = {}
for sub in l:
inner = d
for elem in sub[:-1]:
inner = inner.setdefault(elem, {})
inner[sub[-1]] = ""
If for some reason you really want this as a recursive function, then the following is an equivalent version. It starts off with a base dict, and with each call creates an inner dict and the next call goes down one level of the dict and passes the rest of the list. The base-case is when the list has one element so the string is used instead of a dict. Again, for simplicity, setdefault is used:
def create_dict(l, d):
if len(l) == 1:
d[l[0]] = ""
else:
d = d.setdefault(l[0], {})
create_dict(l[1:], d)
d = {}
for sub in l:
create_dict(sub, d)
Try to avoid using built-in names for variables. Both list and dict represent the respective class' constructor which are not any more available in your program.
I did this rudimentary logic recursion function. Which I tested for your sample input to work well.
def subdict(dic,val):
if len(val)==1:
dic.update({val[0]:""})
else:
if val[0] not in dic.keys():
dic.update({val[0]:{}})
subdict(dic[val[0]],val[1:])
full execution:
lists = [
['1', '2', '3'],
['a', 'b'],
['I', 'II'],
['A', 'B', 'C'],
['A', 'B', 'D']
]
rootdict = {}
def subdict(dic,val):
if len(val)==1:
dic.update({val[0]:""})
else:
if val[0] not in dic.keys():
dic.update({val[0]:{}})
subdict(dic[val[0]],val[1:])
for li in lists:
subdict(rootdict,li)
print(rootdict)
Output:
{'1': {'2': {'3': ''}}, 'a': {'b': ''}, 'I': {'II': ''}, 'A': {'B': {'C': '', 'D': ''}}}
Explainaton:
subdict function :
Checks if we have reached the end, then it happily terminates this leaf of recursion by adding the final entry of key with value ''
if we aren't at the end leaf, it checks if the key[0] is not present in this level of the dict and if so it adds that key. Then finally recursively proceeds for the next iteration, further deep down.
I know this is boring logic, but it works :)
As Tomerikoo said, you don't have to use a recursion, but if you want to solve it using recursion, you should define a recursion function, with your base case - last element in a list.
Then iterate your input (list of lists), and pass the current working list to the recursive function.
UPDATE: thanks to a bug Tomerikoo found, I had to fix my answer. When you use .update() method of a dict, it doesn't do "deepcopy" of the values. So you have to implement it yourself, with another recursion :-)
You need to implement a merge_dict function that, before updating the result. The function below takes two dicts, and merge them using deepcopy. I'll try to simplify the steps:
Iterate the second dict items, if the value is a dict then -
Check if the key already exists in the result, if not create an empty dict, and call the merge function again
If the value is not a dict - simply add it to the result dict.
import json
from copy import deepcopy
final_dict = {}
my_list = [
['1', '2', '3'],
['a', 'b'],
['I', 'II'],
['A', 'B', 'C'],
['A', 'B', 'D']
]
def nested_dict(a_list):
if len(a_list) == 1:
print("base case: {}".format(a_list))
return {a_list[0]: ""}
return {a_list[0]: nested_dict(a_list[1:])}
def merge_dicts(d1, d2):
res = deepcopy(d1)
for k, v in d2.items():
if isinstance(v, dict):
res[k] = merge_dicts(res.get(k, {}), v)
else:
res[k] = v
return res
for sub in my_list:
my_dict = nested_dict(sub)
final_dict = merge_dicts(final_dict, my_dict)
print("final dict: {}".format((json.dumps(final_dict, sort_keys=True, indent=4))))
I want to create two dictionaries in python by dictionary comprehension at the same time. The two dictionaries share the same key set, but have different values for each key. Therefore, I use a function to return a tuple of two values, and hoping a dictionary comprehension can create these two dictionaries at the same time.
Say, I have a function
def my_func(foo):
blablabla...
return a, b
And I will create two dictionaries by
dict_of_a, dict_of_b = ({key:my_func(key)[0]}, {key:my_func(key)[1]} for key in list_of_keys)
Is there any better code to improve it? In my opinion, my_func(key) will be called twice in each iteration, slowing down the code. What is the correct way to do it?
With ordered slicing:
def myfunc(k):
return k + '0', k + '1'
list_of_keys = ['a', 'b', 'c']
groups = [(k,v) for k in list_of_keys for v in myfunc(k)]
dict_of_a, dict_of_b = dict(groups[::2]), dict(groups[1::2])
print(dict_of_a) # {'a': 'a0', 'b': 'b0', 'c': 'c0'}
print(dict_of_b) # {'a': 'a1', 'b': 'b1', 'c': 'c1'}
for key in list_of_keys:
dict_of_a[key],dict_of_b[key] = my_func(key)
The regular loop is probably the best way to go. If you want to play with functools, you can write:
>>> def func(foo): return foo[0], foo[1:]
...
>>> L = ['a', 'ab', 'abc']
>>> functools.reduce(lambda acc, x: tuple({**d, x: v} for d, v in zip(acc, func(x))), L, ({}, {}))
({'a': 'a', 'ab': 'a', 'abc': 'a'}, {'a': '', 'ab': 'b', 'abc': 'bc'})
The function reduce is a fold: it takes the current accumulator (here the dicts being built) and the next value from L:
d, v in zip(acc, func(x)) extracts the dicts one at a time and the matching element of the return value of func;
{**d, x: v} update the dict with the current value.
I don't recommend this kind of code since it's hard to maintain.
my_func(key) will be called twice in each iteration, slowing down the code
Dont worry about it. Unless you need to do thousands/millions of iterations and the script takes an unreasonably long time to complete, you shouldn't concern with negligible optimization gains.
That said, I'd use something like this:
if __name__ == '__main__':
def my_func(k):
return f'a{k}', f'b{k}'
keys = ['x', 'y', 'z']
results = (my_func(k) for k in keys)
grouped_values = zip(*results)
da, db = [dict(zip(keys, v)) for v in grouped_values]
print(da)
print(db)
# Output:
# {'x': 'ax', 'y': 'ay', 'z': 'az'}
# {'x': 'bx', 'y': 'by', 'z': 'bz'}
You cannot create two dicts in one dict comprehension.
If your primary goal is to just call my_func once to create both dicts, use a function for that:
def mkdicts(keys):
dict_of_a = {}
dict_of_b = {}
for key in keys:
dict_of_a[key], dict_of_b[key] = my_func(key)
return dict_of_a, dict_of_b
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)
All the questions I've seen do the exact opposite of what I want to do:
Say I have a list:
lst = ['a','b','c']
I am looking to make a dictionary where the key is the element number (starting with 1 instead of 0) and the list element is the value. Like this:
{1:'a', 2:'b', 3:'c'}
But for a long list. I've read a little about enumerate() but everything I've seen has used the list element as the key instead.
I found this:
dict = {tuple(key): idx for idx, key in enumerate(lst)}
But that produces:
{'a':1, 'b':2, 'c':3}
... which is the opposite of what I want. And, also in a weird notation that is confusing to someone new to Python.
Advice is much appreciated! Thanks!
enumerate has a start keyword argument so you can count from whatever number you want. Then just pass that to dict
dict(enumerate(lst, start=1))
You could also write a dictionary comprehension
{index: x for index, x in enumerate(lst, start=1)}
By default enumerate start from 0 , but you can set by this value by second argument which is start , You can add +1 to every iterator if you want to start from 1 instead of zero :
print({index+1:value for index,value in enumerate(lst)})
output:
{1: 'a', 2: 'b', 3: 'c'}
Above dict comprehension is same as :
dict_1={}
for index,value in enumerate(lst):
dict_1[index+1]=value
print(dict_1)
Using Dict Comprehension and enumerate
print({x:y for x,y in enumerate(lst,1)})
{1: 'a', 2: 'b', 3: 'c'}
Using Dict Comprehension , zip and range-
print({x:y for x,y in zip(range(1,len(lst)+1),lst)})
{1: 'a', 2: 'b', 3: 'c'}
I think the below code should help.
my_list = ['A', 'B', 'C', 'D']
my_index = []
my_dict = {}
for i in range(len(my_list)):
my_index.append(i+1)
for key in my_index:
for value in my_list:
my_dict[key] = value
I have a list like this :
list1 = ["a:b","x:y","s:e","w:x"]
I want convert into dictionary like this:
dict = {'a':'b', 'x':'y','s':'e','w':'x'}
The list is dynamic. How could i achieve this ?
You could do
>>> list1 = ["a:b","x:y","s:e","w:x"]
>>> dict(elem.split(':') for elem in list1)
{'a': 'b', 'x': 'y', 's': 'e', 'w': 'x'}
Like this?
super_dict = dict()
for el in list1:
super_dict[el[0]] = el[-1]
Of course there could be problems if the keys are identical, you'll need to add code if needed