I have a list Final_Bioteck whose first 5 enteries looks like this:
['PBYI', 'DRRX', 'FBIO', 'CAPR', 'KDMN']
I'm trying to create a nested dictionary with the first layer being the letters of the alphabet and the second layer being an element from the above list.
I'm trying to create an indexed dictionary.
I attempted to do so with the code below:
from string import ascii_uppercase
Bioteck_dict ={}
for letter in ascii_uppercase:
for stock in Final_Bioteck:
if stock.startswith(letter):
try:
Bioteck_dict[letter].update(stock)
except KeyError:
Bioteck_dict[letter] = stock
However, I'm getting the following error:
'str' object has no attribute 'update'
Desired output is something like this:
Bioteck_dict
{A:
B:
C: 'CAPR':{}
D: 'DRRX':{}
E:
F:
or even this:
{A:
B:
C: 'CAPR'
D: 'DRRX'
E:
F: 'FBIO':
}
.update() is a method for dicts, and you're calling it on a string. This is a way of doing what you appear to want:
Final_Bioteck = ['PBYI', 'DRRX', 'FBIO', 'CAPR', 'KDMN']
Bioteck_dict = {}
for stock in Final_Bioteck:
Bioteck_dict.setdefault(stock[0], {})[stock] = {}
Following the update to your question, it seems like you want letters that have no associated stocks to nonetheless be explicitly represented in the dict as empty containers. It's questionable whether you really need that (most things you would subsequently want to do will involve just iterating over what is in the dict) but you can get that effect in one of two ways:
EITHER:
Final_Bioteck = ['PBYI', 'DRRX', 'FBIO', 'CAPR', 'KDMN']
from collections import defaultdict
Bioteck_dict = defaultdict(dict) # now you can look up any letter (or any key at all) and receive the empty dict, even if there was no entry there before
for stock in Final_Bioteck:
Bioteck_dict[stock[0]][stock] = {}
OR:
Final_Bioteck = ['PBYI', 'DRRX', 'FBIO', 'CAPR', 'KDMN']
Bioteck_dict = {letter:{} for letter in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'}
for stock in Final_Bioteck:
Bioteck_dict[stock[0]][stock] = {}
The problem is that you're assigning each dict value as a string, not a dict. It needs to start off as a dict so you can update it. 'FBIO'.update(blah) makes no sense. Additionally, you probably want a dict of lists, rather than a dict of dicts—what would the key be, after all?
from collections import defaultdict
Bioteck_dict = defaultdict(list) # Or `set`, if unique and unordered.
for stock in Final_Bioteck:
Bioteck_dict[stock[0]].append(stock)
The result is:
{'C': ['CAPR'], 'D': ['DRRX'], 'F': ['FBIO'], 'K': ['KDMN'], 'P': ['PBYI']}
This one is a bit more akin to your logic:
from string import ascii_uppercase
stocks = ['PBYI', 'DRRX', 'FBIO', 'CAPR', 'KDMN']
d = {}
for letter in ascii_uppercase:
d[letter] = {}
for stock in stocks:
if stock.startswith(letter):
d[letter][stock] = {}
print d
That returns:
{'A': {}, 'C': {'CAPR': {}}, 'B': {}, 'E': {}, 'D': {'DRRX': {}}, 'G': {}, 'F': {'FBIO': {}}, 'I': {}, 'H': {}, 'K': {'KDMN': {}}, 'J': {}, 'M': {}, 'L': {}, 'O': {}, 'N': {}, 'Q': {}, 'P': {'PBYI': {}}, 'S': {}, 'R': {}, 'U': {}, 'T': {}, 'W': {}, 'V': {}, 'Y': {}, 'X': {}, 'Z': {}}
Using dict comprehension, if unique records.
l = ['PBYI', 'DRRX', 'FBIO', 'CAPR', 'KDMN']
d = { i[0]:i for i in l}
pprint(d)
Output:
{'C': 'CAPR', 'D': 'DRRX', 'F': 'FBIO', 'K': 'KDMN', 'P': 'PBYI'}
Use defaultdict if multiple records.
l = ['PBYI', 'DRRX', 'FBIO', 'CAPR', 'KDMN', 'DUMMY','CAT','COLD']
from collections import defaultdict
d = defaultdict(list)
for i in l:
d[i[0]].append(i)
pprint(d)
Output:
defaultdict(<class 'list'>,
{'C': ['CAPR', 'CAT', 'COLD'],
'D': ['DRRX', 'DUMMY'],
'F': ['FBIO'],
'K': ['KDMN'],
'P': ['PBYI']})
Related
I have this trie dictionary:
{'s': {'h': {'o': {'w': {'value': 'a programm'},
'o': {'t': {'value':'a bullet'}},
'e': {'value': 's.th to wear'}},
'a': {'m': {'e': {'value': 'a feeling'}}},
'i': {'t': {'value': 'deficate'}}}}}
I want to define a function that searches through this trie dict and finds its values.
how to do it ?
here is how I do it:
def finder(dict_trie,word):
if word:
first,rest = word[0],word[1:]
finder(dict_trie[first],rest)
else:
print(dict_trie["value"])
finder(dict_trie,"shit")
result = deficate
There is a dictionary as the input based on which the second dictionary is formed in the following code:
dict1 = {'a': 'ok', 'b': 'ok', 'c': 'needs repair'}
final_dict = {}
for d in dict1.items():
if d[1] == 'ok':
final_dict[d[0]] = 'needs repair'
else:
final_dict[d[0]] = 'ok'
print(final_dict)
where it prints out:
{'a': 'needs repair', 'b': 'needs repair', 'c': 'ok'}
How to change the for loop to a list comprehension?
You can use a dictionary comprehension - list comprehensions make not much sense here:
# what to change into what
flipper = {"ok":"needs repair", "needs repair":"ok"}
source = {'a': 'ok', 'b': 'ok', 'c': 'needs repair'}
flipped = { key:flipper[value] for key, value in source.items()}
print(source)
print(flipped)
Output:
{'a': 'ok', 'b': 'ok', 'c': 'needs repair'}
{'a': 'needs repair', 'b': 'needs repair', 'c': 'ok'}
You could re-write your for loop into a dictionary comprehension, like this:
dict1 = {'a': 'ok', 'b': 'ok', 'c': 'needs repair'}
result = {k: 'needs repair' if v == 'ok' else 'ok' for k, v in dict1.items()}
print(result)
Output
{'a': 'needs repair', 'b': 'needs repair', 'c': 'ok'}
You can do it with a single dict comprehension as follows:
>>> final_dict = {k: 'needs repair' if v == 'ok' else 'ok' for k, v in dict1.items()}
>>> final_dict
{'a': 'needs repair', 'b': 'needs repair', 'c': 'ok'}
>>>
In practice, I'd split the long line:
final_dict = {k: 'needs repair' if v == 'ok' else 'ok'
for k, v in dict1.items()}
It would be better choice if you would've asked for dictionary comprehension
final_dict = { k: 'needs repair' if v=='ok' else 'ok' for k,v in dict1.items()}
Suppose you have the following dictionaries
dict1 = {'a': 'a'}
dict2 = {'a':'b', 'c':'d' , 'f':'w', 'r':' not unique' }
dict3 = {'a': 'c', 'c':'e'}
dict4 = {'a': 'd', 'c':'e', 'f':'r'}
dict5 = {'r': 'x', 'y':'only unique', 'f':'r'}
The intended result is as follows:
{'y': 'only unique', 'a': 'abcd', 'c': 'dee', 'f': 'wrr', 'r': ' not uniquex'}
You want to merge them in a particular order as they appear. If the key is repeated, one wants to concatenate the results. The use case is in 10K submissions. Often you find this
Column X Column X+1
(123)
when the number is (123)
I tried this and it works
# helper functions
def string_comprehension(array_like,separator,separate_chars=False):
if not isinstance(array_like,list):
array_like = to_list(array_like)
if separate_chars:
result = ''.join(separator.join(map(str, x)) for x in array_like)
else:
result = separator.join(str(x) for x in array_like)
return result
def to_list(var):
a = []
if islist(var): return var
else: return [var]
def merge_concatenate_dicts(dict_list, separator = ''):
def update_common_dict(local_common_keys,global_common_dict,index_list):
for key in list(local_common_keys):
if key in global_common_dict.keys():
current_indices = to_list(global_common_dict.get(key))
new_indices = sorted(list(set.union(set(current_indices),
set(index_list))))
common_keys_dict.update( { key: new_indices} )
else:
common_keys_dict.update( { key: index_list} )
return global_common_dict
common_keys_dict = {}
dict_combination = it.combinations(dict_list,2)
disjoint_keys = []
for d_combine in dict_combination:
index_list = sorted([dict_list.index(d_combine[0]),dict_list.index(d_combine[1])])
keys_dict1 = list(d_combine[0].keys())
keys_dict2 = list(d_combine[1].keys())
common_keys = set.intersection( set(keys_dict1),
set(keys_dict2))
if common_keys_dict:
all_common_keys = list(set.union(
common_keys,
set(list(common_keys_dict.keys())
)))
else: all_common_keys = list(common_keys)
all_keys = list(reduce(set.union,
map(set,[keys_dict1, keys_dict2] )))
disjoint_local = [k for k in all_keys if k not in all_common_keys]
disjoint_universe = list(set.union(set(disjoint_local), set(disjoint_keys)))
disjoint_keys = [k for k in disjoint_universe if k not in all_common_keys]
common_keys_dict = update_common_dict(local_common_keys = common_keys,
global_common_dict = common_keys_dict, index_list = index_list )
merged_dicts = {}
for k_disjoint in disjoint_keys:
for d in dict_list:
if k_disjoint in d:
merged_dicts.update({k_disjoint : d.get(k_disjoint)})
break
for k_common in common_keys_dict.keys():
dict_merge_list = [d.get(k_common) for idx,d in enumerate(dict_list) if idx in common_keys_dict.get(k_common)]
merged_val = string_comprehension(array_like=dict_merge_list, separator= separator)
merged_dicts.update( {k_common: merged_val} )
return merged_dicts
dict1 = {'a': 'a'}
dict2 = {'a':'b', 'c':'d' , 'f':'w', 'r':' not unique' }
dict3 = {'a': 'c', 'c':'e'}
dict4 = {'a': 'd', 'c':'e', 'f':'r'}
dict5 = {'r': 'x', 'y':'only unique', 'f':'r'}
result = merge_concatenate_dicts([dict1,dict2,dict3,dict4,dict5])
print(result)
Yields
{'y': 'only unique', 'a': 'abcd', 'c': 'dee', 'f': 'wrr', 'r': ' not uniquex'}
You can use collections.defaultdict here.
from collections import defaultdict
dict1 = {'a': 'a'}
dict2 = {'a':'b', 'c':'d' , 'f':'w', 'r':' not unique' }
dict3 = {'a': 'c', 'c':'e'}
dict4 = {'a': 'd', 'c':'e', 'f':'r'}
dict5 = {'r': 'x', 'y':'only unique', 'f':'r'}
def merge(*dicts):
out=defaultdict(str)
for d in dicts:
for k,v in d.items():
out[k]+=v
return out #The return type is defaultdict if you want dict then use `return dict(out)`
merge(dict1,dict2,dict3,dict4,dict5)
# defaultdict(<class 'str'>, {'a': 'abcd', 'c': 'dee', 'f': 'wrr', 'r': ' not uniquex', 'y': 'only unique'})
#When `return dict(out)`
# {'a': 'abcd', 'c': 'dee', 'f': 'wrr', 'r': ' not uniquex', 'y': 'only unique'}
I have a nested dictionary {1: {2: {3: None}}} and a dictionary that maps keys of the nested dictionary to a set of values such as {1: x, 2: y, 3: z}. I want to transform the nested dictionary to this form {x: {y: {z: None}}}. I have tried a couple of recursive functions but I keep going in circles and confusing myself. What is the best way to achieve this?
The level of nesting is arbitrary. The above is a simple example.
You need to recurse through the dictionary while building a new one with new keys. Note that if you have a list or tuple in there somewhere that has other dictionaries in it, they won't be processed - you'd have to add some code to do that. You can actually do this without building a new dictionary, but I think this way is simpler.
od = { 1: { 2: { 3: None }}}
kd = { 1: 'x', 2: 'y', 3: 'z' }
def replace_keys(old_dict, key_dict):
new_dict = { }
for key in old_dict.keys():
new_key = key_dict.get(key, key)
if isinstance(old_dict[key], dict):
new_dict[new_key] = replace_keys(old_dict[key], key_dict)
else:
new_dict[new_key] = old_dict[key]
return new_dict
nd = replace_keys(od, kd)
print nd
outputs:
{'x': {'y': {'z': None}}}
The accepted answer will not support dict of list, adding the full feature
#bilentor,
od = {'name': 'John', '1': [{'name': 'innername'}]}
kd = { 'name': 'cname', '1': '2', 3: 'z' }
def replace_keys(data_dict, key_dict):
new_dict = { }
if isinstance(data_dict, list):
dict_value_list = list()
for inner_dict in data_dict:
dict_value_list.append(replace_keys(inner_dict, key_dict))
return dict_value_list
else:
for key in data_dict.keys():
value = data_dict[key]
new_key = key_dict.get(key, key)
if isinstance(value, dict) or isinstance(value, list):
new_dict[new_key] = replace_keys(value, key_dict)
else:
new_dict[new_key] = value
return new_dict
nd = replace_keys(od, kd)
print(nd)
You can use a NestedDict
from ndicts import NestedDict
d = {1: {2: {3: None}}}
replace = {1: 'x', 2: 'y', 3: 'z'}
def ndict_replace(ndict: dict, map: dict):
nd = NestedDict(nd)
new_nd = NestedDict()
for key, value in nd.items():
new_key = tuple(replace.get(k, k) for k in key)
new_nd[new_key] = value
return new_nd.to_dict()
>>> ndict_replace(d, replace)
{'x': {'y': {'z': None}}}
The solution is robust and works with any nested dictionary
>>> d = {
1: {2: {3: None}},
3: {4: None},
5: None
}
>>> ndict_replace(d, replace)
{'x': {'y': {'z': None}}, 'z': {4: None}, 4: None}}
To install ndicts pip install ndicts
exhaustive:
- all keys in the dictionary, even if the keys are in a nested dictionary that is a value to a previous-level dictionary key.
sorted:
- this is to ensure the keys are always returned in the same order
The nesting is arbitrarily deep. A non-recursive algorithm is preferred.
level1 = {
'a' : 'aaaa',
'level2_1' : {'b': 'bbbbb', 'level3': {'c': 'cccc', 'd': 'dddddd'} },
'level2_2' : { 'z': 'zzzzzzz' }
}
Note: dictionary values can include lists (which can have dictionaries as elements), e.g.
tricky = {'category': [{'content': 'aaaaa'}, {'content': 'bbbbbb'}]}
def _auxallkeys(aset, adict):
aset.update(adict)
for d in adict.itervalues():
if isinstance(d, dict):
_auxallkeys(aset, d)
def allkeys(adict):
aset = set()
_auxallkeys(aset, adict)
return sorted(aset)
is the obvious (recursive) solution. To eliminate recursion:
def allkeys(adict):
aset = set()
pending = [adict]
while pending:
d = pending.pop()
aset.update(d)
for dd in d.itervalues():
if isinstance(dd, dict):
pending.append(dd)
return sorted(aset)
since the order of processing of the various nested dicts does not matter for this purpose.
Edit: the OP comments whining that it doesn't work if a dict is not nested, but rather in a list (and I replied that it could also be in a tuple, an object with attributes per-instance or per-class [maybe a base class thereof], a shelf, and many other ways to hide dicts around the house;-). If the OP will deign to define precisely what he means by "nested" (obviously not the same meaning as ordinary mortals apply to the word in question), it will probably be easier to help him. Meanwhile, here's a version that covers lists (and tuples, but not generators, instances of many itertools classes, shelves, etc, etc);
def allkeys(adict):
aset = set()
pending = [adict]
pendlis = []
def do_seq(seq):
for dd in seq:
if isinstance(dd, dict):
pending.append(dd)
elif isinstance(dd, (list, tuple)):
pendlis.append(dd)
while pending or pendlis:
while pending:
d = pending.pop()
aset.update(d)
do_seq(d.itervalues())
while pendlis:
l = pendlis.pop()
do_seq(l)
return sorted(aset)
A non-recursive method isn't obvious to me right now. The following works on your original example. Edit: It will now handle dicts within a list within a dict, at least the one within the tricky example cited in the comment to Alex Martelli's answer.
#!/usr/bin/env python
import types
def get_key_list(the_dict, key_list):
for k, v in (the_dict.iteritems()):
key_list.append(k)
if type(v) is types.DictType:
get_key_list(v, key_list)
if type(v) is types.ListType:
for lv in v:
if type(lv) is types.DictType:
get_key_list(lv, key_list)
return
level1 = {
'a' : 'aaaa',
'level2_1' : {'b': 'bbbbb', 'level3': {'c': 'cccc', 'd': 'dddddd'} },
'level2_2' : { 'z': 'zzzzzzz' }
}
tricky = {'category': [{'content': 'aaaaa'}, {'content': 'bbbbbb'}]}
key_list = []
get_key_list(level1, key_list)
key_list.sort()
print key_list
key_list = []
get_key_list(tricky, key_list)
key_list.sort()
print key_list
Output:
['a', 'b', 'c', 'd', 'level2_1', 'level2_2', 'level3', 'z']
['category', 'content', 'content']
Here's a non-recursive solution which processes generators as well as lists, tuples and dicts and adds all successive keys if a key appears more than once:
def get_iterator(i):
if hasattr(i, 'next'):
# already an iterator - use it as-is!
return i
elif hasattr(i, '__iter__') and not isinstance(i, basestring):
# an iterable type that isn't a string
return iter(i)
else:
# Can't iterate most other types!
return None
def get_dict_keys(D):
LRtn = []
L = [(D, get_iterator(D))]
while 1:
if not L: break
cur, _iter = L[-1]
if _iter:
# Get the next item
try:
i = _iter.next()
except StopIteration:
del L[-1]
continue
if isinstance(cur, dict):
# Process a dict and all subitems
LRtn.append(i)
_iter = get_iterator(cur[i])
if _iter: L.append((cur[i], _iter))
else:
# Process generators, lists, tuples and all subitems
_iter = get_iterator(i)
if _iter: L.append((i, _iter))
# Sort and return
LRtn.sort()
return LRtn
D = {
'a' : 'aaaa',
'level2_1' : {'b': 'bbbbb', 'level3': {'c': 'cccc', 'd': 'dddddd', 'e': 134, 'f': [{'blah': 553}]} },
'level2_2' : { 'z': 'zzzzzzz' },
'blah2': iter([{'blah3': None}]),
}
print get_dict_keys(D)
EDIT: Increased the speed a bit and made the code shorter.
I also prefer a recursive approach...
#!/usr/bin/env python
def extract_all_keys(structure):
try:
list_of_keys = structure.keys()
for value in structure.values():
add_all_keys_in_value_to_list(value, list_of_keys)
except AttributeError:
list_of_keys = []
return list_of_keys.sort()
def add_all_keys_in_value_to_list(value, list_of_keys):
if isinstance(value, dict):
list_of_keys += extract_all_keys(value)
elif isinstance(value, (list, tuple)):
for element in value:
list_of_keys += extract_all_keys(element)
import unittest
class TestKeys(unittest.TestCase):
def given_a_structure_of(self, structure):
self.structure = structure
def when_keys_are_extracted(self):
self.list_of_keys = extract_all_keys(self.structure)
def testEmptyDict(self):
self.given_a_structure_of({})
self.when_keys_are_extracted()
self.assertEquals(self.list_of_keys, [])
def testOneElement(self):
self.given_a_structure_of({'a': 'aaaa'})
self.when_keys_are_extracted()
self.assertEquals(self.list_of_keys, ['a'])
def testTwoElementsSorted(self):
self.given_a_structure_of({
'z': 'zzzz',
'a': 'aaaa',
})
self.when_keys_are_extracted()
self.assertEquals(self.list_of_keys, ['a', 'z'])
def testNestedElements(self):
self.given_a_structure_of({
'a': {'aaaa': 'A',},
'b': {'bbbb': 'B',},
})
self.when_keys_are_extracted()
self.assertEquals(self.list_of_keys, ['a', 'aaaa', 'b', 'bbbb'])
def testDoublyNestedElements(self):
self.given_a_structure_of({
'level2': {'aaaa': 'A',
'level3': {'bbbb': 'B'}
}
})
self.when_keys_are_extracted()
self.assertEquals(self.list_of_keys, ['aaaa', 'bbbb', 'level2', 'level3'])
def testNestedExampleOnStackOverflow(self):
level1 = {
'a' : 'aaaa',
'level2_1' : {'b': 'bbbbb', 'level3': {'c': 'cccc', 'd': 'dddddd'} },
'level2_2' : { 'z': 'zzzzzzz' }
}
self.given_a_structure_of(level1)
self.when_keys_are_extracted()
self.assertEquals(self.list_of_keys, ['a', 'b', 'c', 'd', 'level2_1', 'level2_2', 'level3', 'z'])
def testListExampleOnStackOverflow(self):
tricky = {'category': [{'content': 'aaaaa'}, {'content': 'bbbbbb'}]}
self.given_a_structure_of(tricky)
self.when_keys_are_extracted()
self.assertEquals(self.list_of_keys, ['category', 'content', 'content'])
def testTuplesTreatedLikeLists(self):
tricky_tuple = {'category': ({'content': 'aaaaa'}, {'content': 'bbbbbb'})}
self.given_a_structure_of(tricky_tuple)
self.when_keys_are_extracted()
self.assertEquals(self.list_of_keys, ['category', 'content', 'content'])
def testCanHandleString(self):
self.given_a_structure_of('keys')
self.when_keys_are_extracted()
self.assertEquals(self.list_of_keys, [])
if __name__ == '__main__':
unittest.main()
I think it is better to use a recursive method.
My code is in the following.
level1 = {
'a' : 'aaaa',
'level2_1' : {'b': 'bbbbb', 'level3': {'c': 'cccc', 'd': 'dddddd'} },
'level2_2' : { 'z': 'zzzzzzz' }
}
all_keys=[] # a global list to store all the keys in level1
def depth ( dict ):
for k in dict:
if type(dict[k]) == type(dict): #judge the type of elements in dictionary
depth(dict[k]) # recursive
else:
all_keys.append(k)
depth(level1)
print all_keys