a string to a dictionary of dictionary - python

Hi I want to create a dictionary of dictionary but I can't for a text and in an iterative way…. and I am obligated to create a lot of variable and I want that the creation of the dictionary to be automatical without the creation of variables. Can you help me to improve it ?
I tried to create two functions and create this dictionary with one word…
def create_last_dictionary(word, n):
dico={}
dico[word[len(word) - n]] = {}
return dico
def create_dictionary(dic_to_add, word, n):
dic = {}
dic[word[len(word) - n]]=dic_to_add
return dic
word = "mommy"
one = create_last_dictionary(word, 1)
two = create_dictionary(one, word, 2)
three = create_dictionary(two, word, 3)
four = create_dictionary(three, word, 4)
five = create_dictionary(four, word, 5)
six = create_dictionary(five, word, 6)
seven = create_dictionary(six, word, 7)
result :
{'m': {'o': {'r': {'n': {'i': {'n': {'g': {}}}}}}}}
I want it for a list of words like :
if the list is : ["good", "Morning", "mommy"]
I want the dictionary to be :
{{'g': {'o': {'o': {'d': {}}}}}, 'm': {'o': {'m': {'m': {'y': {}}}}, {'r': {'n': {'i': {'n': {'g': {}}}}}}}}
the representation of the dictionary :
{
{'g': {'o': {'o': {'d': {}}}}},
{'m': {'o': {{'m': {'m': {'y': {}}}},
{'r': {'n': {'i': {'n': {'g': {}}}}}}}}}
}

You need to create a function which inserts a new word in an existing (maybe empty) tree. To achieve this I propose a recursive function:
def insert(tree, path):
if path:
insert(tree.setdefault(path[0], {}), path[1:])
Now you can use it:
tree = {}
insert(tree, 'good')
insert(tree, 'morning')
insert(tree, 'mommy')
print(tree)
prints
{'m': {'o': {'m': {'m': {'y': {}}},
'r': {'n': {'i': {'n': {'g': {}}}}}}},
'g': {'o': {'o': {'d': {}}}}}
EDIT:
In case you don't like using .setdefault() because it seems a hard-to-read shortcut, consider this:
def insert(tree, path):
if path:
if path[0] not in tree:
tree[path[0]] = {}
insert(tree[path[0]], path[1:])

Instead of chaining function calls, you could create a recursive function; your are almost there as you created two cases already. I found a course about Python recursive functions here: https://www.python-course.eu/python3_recursive_functions.php

Related

Pytest dict equality preserving order and nice diff output (python-3.7+)

As of python 3.7, dict is guaranteed to be insertion ordered. I have a unit test using pytest where I would like to compare two dicts but with order of elements in mind.
My application will never run on python older then 3.7 so backward compatibility is not an issue for me.
Question
Is there any possibility to compare regular dicts, preserving order and nice diff output in pytest?
My unsuccesful tries
a = {'x': 1, 'y': {'u': 3, 'v': 4}}
b = {'y': {'v': 4, 'u': 3}, 'x': 1}
assert a == b
This assertion passes, but I don't want it to. These dicts are equal but with wrong order.
I can convert everything to OrderedDict but it is
ugly code
produces ugly pytest diff output
from collections import OrderedDict
def deep_ordered_dict(value):
if isinstance(value, dict):
value = OrderedDict(value)
for k, v in value.items():
value[k] = deep_ordered_dict(v)
if isinstance(value, list):
for k, v in enumerate(value):
value[k] = deep_ordered_dict(v)
return value
a = {'x': 1, 'y': {'u': 3, 'v': 4}}
b = {'y': {'v': 4, 'u': 3}, 'x': 1}
assert deep_ordered_dict(a) == deep_ordered_dict(b)
Comparing it as JSON is not better
produces ugly pytest diff output
import json
a = {'x': 1, 'y': {'u': 3, 'v': 4}}
b = {'y': {'v': 4, 'u': 3}, 'x': 1}
assert json.dumps(a, indent=2) == json.dumps(b, indent=2)
Even though dictionaries are required to preserve insertion order as of Python 3.7, for backward compatibility equality comparisons are order-independent. This is one of the documented differences between dict and OrderedDict, so if you want order-sensitive comparisons you should use the latter.

Append dynamic dictionary to a list in Python in a for loop

This is initial program for implementing Prefix Tree for a Question. Later i used the dict.copy() function to deal with dynamic behavior of dictionary, but couldn't get the desired output .
end='end'
def make_trie(word,root):
current_dict=root
for letter in word:
current_dict=current_dict.setdefault(letter,{})
current_dict[end]=end
return root
s=[]
n=int(input())
t=[]
for _ in range(n):
s.append(input())
if _==0:
d=make_trie(s[-1],{})
else:
d=make_trie(s[-1],d)
t.append(d.copy())
print(t)
List i am getting for input :
4
abcd
abce
abcdex
abcde
is :
[{'a': {'b': {'c': {'d': {'end': 'end', 'e': {'x': {'end': 'end'}, 'end': 'end'}}, 'e': {'end': 'end'}}}}}, {'a': {'b': {'c': {'d': {'end': 'end', 'e': {'x': {'end': 'end'}, 'end': 'end'}}, 'e': {'end': 'end'}}}}}, {'a': {'b': {'c': {'d': {'end': 'end', 'e': {'x': {'end': 'end'}, 'end': 'end'}}, 'e': {'end': 'end'}}}}}, {'a': {'b': {'c': {'d': {'end': 'end', 'e': {'x': {'end': 'end'}, 'end': 'end'}}, 'e': {'end': 'end'}}}}}]
Which is 4 times the final Dictionary.
Please suggest some way to deal with this issue.
Since the trie is a dictionary of dictionaries, you need to deep copy instead of shallow. Try this:
from copy import deepcopy
end='end'
def make_trie(word,root):
current_dict=root
for letter in word:
current_dict=current_dict.setdefault(letter,{})
current_dict[end]=end
return root
s=[]
n=int(input())
t=[]
for _ in range(n):
s.append(input())
if _==0:
d=make_trie(s[-1],{})
else:
d=make_trie(s[-1],d)
t.append(deepcopy(d))
print(t)
When you shallow copy, you just copy the outermost dictionary, so the inner dictionaries are still shared between copied dictionaries.
Another option would be to cast the dictionary object to a string and back, using the Python 3's eval function or the inbuilt ast library's function literal_eval, if your dictionary is not too big. You can try the following:
end='end'
def make_trie(word,root):
current_dict=root
for letter in word:
current_dict=current_dict.setdefault(letter,{})
current_dict[end]=end
return root
s=[]
n=int(input())
t=[]
for _ in range(n):
s.append(input())
if _==0:
d=make_trie(s[-1],{})
else:
d=make_trie(s[-1],d)
d_str = str(d)
t.append(eval(d_str))
print(t)

How to make replacement in python's dict?

The goal I want to achieve is to exchange all items whose form is #item_name# to the from (item_value) in the dict. I use two dict named test1 and test2 to test my function. Here is the code:
test1={'integer_set': '{#integer_list#?}', 'integer_list': '#integer_range#(?,#integer_range#)*', 'integer_range': '#integer#(..#integer#)?', 'integer': '[+-]?\\d+'}
test2={'b': '#a#', 'f': '#e#', 'c': '#b#', 'e': '#d#', 'd': '#c#', 'g': '#f#', 'a': 'correct'}
def change(pat_dict:{str:str}):
print('Expanding: ',pat_dict)
num=0
while num<len(pat_dict):
inv_pat_dict = {v: k for k, v in pat_dict.items()}
for value in pat_dict.values():
for key in pat_dict.keys():
if key in value:
repl='#'+key+'#'
repl2='('+pat_dict[key]+')'
value0=value.replace(repl,repl2)
pat_dict[inv_pat_dict[value]]=value0
num+=1
print('Result: ',pat_dict)
change(test1)
change(test2)
sometimes I can get correct result like:
Expanding: {'integer': '[+-]?\\d+', 'integer_list': '#integer_range#(?,#integer_range#)*', 'integer_set': '{#integer_list#?}', 'integer_range': '#integer#(..#integer#)?'}
Result: {'integer': '[+-]?\\d+', 'integer_list': '(([+-]?\\d+)(..([+-]?\\d+))?)(?,(([+-]?\\d+)(..([+-]?\\d+))?))*', 'integer_set': '{((([+-]?\\d+)(..([+-]?\\d+))?)(?,(([+-]?\\d+)(..([+-]?\\d+))?))*)?}', 'integer_range': '([+-]?\\d+)(..([+-]?\\d+))?'}
Expanding: {'c': '#b#', 'f': '#e#', 'e': '#d#', 'b': '#a#', 'g': '#f#', 'd': '#c#', 'a': 'correct'}
Result: {'c': '((correct))', 'f': '(((((correct)))))', 'e': '((((correct))))', 'b': '(correct)', 'g': '((((((correct))))))', 'd': '(((correct)))', 'a': 'correct'}
But most of time I get wrong results like that:
Expanding: {'integer_range': '#integer#(..#integer#)?', 'integer': '[+-]?\\d+', 'integer_set': '{#integer_list#?}', 'integer_list': '#integer_range#(?,#integer_range#)*'}
Result: {'integer_range': '([+-]?\\d+)(..([+-]?\\d+))?', 'integer': '[+-]?\\d+', 'integer_set': '{(#integer_range#(?,#integer_range#)*)?}', 'integer_list': '#integer_range#(?,#integer_range#)*'}
Expanding: {'f': '#e#', 'a': 'correct', 'd': '#c#', 'g': '#f#', 'b': '#a#', 'c': '#b#', 'e': '#d#'}
Result: {'f': '(((((correct)))))', 'a': 'correct', 'd': '(((correct)))', 'g': '((((((correct))))))', 'b': '(correct)', 'c': '((correct))', 'e': '((((correct))))'}
How could I update my code to achieve my goal?
Your problem is caused by the fact that python dictionaries are unordered. Try using a OrderedDict instead of dict and you should be fine. The OrderedDict works just like a normal dict but with ordering retained, at a small performance cost.
Note that while you could create an OrderedDict from a dict literal (like I did here at first), that dict would be unordered, so the ordering might not be guaranteed. Using a list of (key, value) pairs preserves the ordering in all cases.
from collections import OrderedDict
test1=OrderedDict([('integer_set', '{#integer_list#?}'), ('integer_list', '#integer_range#(?,#integer_range#)*'), ('integer_range', '#integer#(..#integer#)?'), ('integer', '[+-]?\\d+')])
test2=OrderedDict([('b', '#a#'), ('f', '#e#'), ('c', '#b#'), ('e', '#d#'), ('d', '#c#'), ('g', '#f#'), ('a', 'correct')])
def change(pat_dict:{str:str}):
print('Expanding: ',pat_dict)
num=0
while num<len(pat_dict):
inv_pat_dict = {v: k for k, v in pat_dict.items()}
for value in pat_dict.values():
for key in pat_dict.keys():
if key in value:
repl='#'+key+'#'
repl2='('+pat_dict[key]+')'
value0=value.replace(repl,repl2)
pat_dict[inv_pat_dict[value]]=value0
num+=1
print('Result: ',pat_dict)
change(test1)
change(test2)
Try this one. Your problem is due to mutating starting dict. You need to change its copy.
test1={'integer_set': '{#integer_list#?}', 'integer_list': '#integer_range#(?,#integer_range#)*', 'integer_range': '#integer#(..#integer#)?', 'integer': '[+-]?\\d+'}
test2={'b': '#a#', 'f': '#e#', 'c': '#b#', 'e': '#d#', 'd': '#c#', 'g': '#f#', 'a': 'correct'}
def change(d):
new_d = d.copy()
for k in d.keys():
for nk, v in new_d.items():
if k in v:
new_d[nk] = v.replace('#{}#'.format(k), '({})'.format(new_d[k]))
return new_d
test1 = change(test1)
test2 = change(test2)

How to modify a Python JSON objects array

Let's assume the following :
sp_sample=[{"t":1434946093036,"v":54.0},{"t":1434946095013,"v":53.0},{"t":1434946096823,"v":52.0}
I wish I could get the following result :
sp_sample=[{"t":1434946093036,"v":5400.0},{"t":1434946095013,"v":5300.0},{"t":1434946096823,"v":5200.0}
In other words, I wish I could iterate through the array and multiple v by a 100 factor.
The following only performs the multiplication on the first item, ie yields 54000 :
for i, a in enumerate(sp_sample):
a[i]['v'] = a[i]['v'] * 100
The sp_sample is of type tuple. Using the following yields the whole array, which is not what I expect :
print sp_sample[0]
Also, tried printing sp_sample :
print sp_sample
Which returns the following (replaced the ....... for brevity) :
([{'t': 1434946093036, 'v': 54.0}, {'t': 1434946095013, 'v': 53.0}, {'t': 1434946096823, 'v': 52.0}, {'t': 1434946098612, 'v': 52.0}, {'t': 1434946100400, 'v': 51.0}, {'t': 1434946102372, 'v': 49.0},........, {'t': 1434947987823, 'v': 15.0}, {'t': 1434947989851, 'v': 12.0}, {'t': 1434947991899, 'v': 10.0}, {'t': 1434947993744, 'v': 5.0}, {'t': 1434947995599, 'v': 0.0}, {'t': 1434947997455, 'v': 0.0}, {'t': 1434947999494, 'v': 0.0}, {'t': 1434948001542, 'v': 0.0}, {'t': 1434948003417, 'v': 0.0}, {'t': 1434948005264, 'v': 0.0}, {'t': 1434948007120, 'v': 0.0}],)
print type(sp_sample) returns :
Simply iterate over the list and update the dictionaries as you go:
sp_sample = [{"t":1434946093036,"v":54.0},{"t":1434946095013,"v":53.0},{"t":1434946096823,"v":52.0}]
for d in sp_sample:
d['v'] *= 100
>>> print(sp_sample)
[{'t': 1434946093036, 'v': 5400.0}, {'t': 1434946095013, 'v': 5300.0}, {'t': 1434946096823, 'v': 5200.0}]
This will bind in turn each dictionary in list (tuple?) sp_sample to d, which you then update in place. You do not need to use enumerate().
Note that you really need to multiply by 100, not 10000, to achieve the output that you have shown.
Update
sp_sample is actually a tuple with a list of dictionaries as its only item. So you need to access the list in the tuple like this:
sp_sample = ([{"t":1434946093036,"v":54.0},{"t":1434946095013,"v":53.0},{"t":1434946096823,"v":52.0}],)
for d in sp_sample[0]: # N.B. access first item of tuple
d['v'] *= 100
>>> print(sp_sample)
[{'t': 1434946093036, 'v': 5400.0}, {'t': 1434946095013, 'v': 5300.0}, {'t': 1434946096823, 'v': 5200.0}]
Or, since the tuple contains only a single item you could just get rid of the tuple by:
sp_sample = sp_sample[0]
for d in sp_sample:
d['v'] *= 100

Translating characters in a string to multiple characters using Python

I have a list of strings with prefix characters representing the multiplying factor for the number. So if I have data like:
data = ['101n', '100m', '100.100f']
I want to use the dictionary
prefix_dict = {'y': 'e-24', 'z': 'e-21', 'a': 'e-18', 'f': 'e-15', 'p': 'e-12',
'n': 'e-9', 'u': 'e-6', 'm': 'e-3', 'c': 'e-2', 'd': 'e-1',
'da': 'e1', 'h': 'e2', 'k': 'e3', 'M': 'e6', 'G': 'e9',
'T': 'e12', 'P': 'e15', 'E': 'e18', 'Z': 'e21', 'Y': 'e24'}
To insert their corresponding strings. When I look at the other questions similar to mine there is one character being translated into another character. Is there a way to use the translate function to translate one character into multiple characters or should I be approaching this differently?
You can use regex for this, this works for 'da' as well:
>>> data = ['101n', '100m', '100.100f', '1d', '1da']
>>> import re
>>> r = re.compile(r'([a-zA-Z]+)$')
>>> for d in data:
print r.sub(lambda m: prefix_dict.get(m.group(1), m.group(1)), d)
...
101e-9
100e-3
100.100e-15
1e-1
1e1
And a non-regex version using itertools.takewhile:
>>> from itertools import takewhile
>>> def find_suffix(s):
return ''.join(takewhile(str.isalpha, s[::-1]))[::-1]
...
>>> for d in data:
sfx = find_suffix(d)
print (d.replace(sfx, prefix_dict.get(sfx, sfx)))
...
101e-9
100e-3
100.100e-15
1e-1
1e1
Try:
for i, entry in enumerate(data):
for key, value in sorted(prefix_dict.items(),
key = lambda x: len(x[0]), reverse=True):
# need to sort the dictionary so that 'da' always comes before 'a'
if key in entry:
data[i] = entry.replace(key, value)
print(data)
This works for arbitrary combinations in the dictionary and the data. If the dictionary key is always only 1 string long, you have lots of other solutions posted here.
import re
data = ['101da', '100m', '100.100f']
prefix_dict = {'y': 'e-24', 'z': 'e-21', 'a': 'e-18', 'f': 'e-15', 'p': 'e-12',
'n': 'e-9', 'u': 'e-6', 'm': 'e-3', 'c': 'e-2', 'd': 'e-1',
'da': 'e1', 'h': 'e2', 'k': 'e3', 'M': 'e6', 'G': 'e9',
'T': 'e12', 'P': 'e15', 'E': 'e18', 'Z': 'e21', 'Y': 'e24'}
comp = re.compile(r"[^\[A-Za-z]")
for ind,d in enumerate(data):
pre = re.sub(comp,"",d)
data[ind] = d.replace(pre,prefix_dict.get(pre))
print data
['101e1', '100e-3', '100.100e-15']
You can use pre = [x for x in d if x.isalpha()][0] instead of using re

Categories