Convert String representation of a List to a Dictionary Python - python

How can I convert a str representation of the list, such as the below string into a dictionary?
a = '[100:0.345,123:0.34,145:0.86]'
Expected output :
{100:0.345,123:0.34,145:0.86}
First tried to convert the string into a list using ast.literal_eval. But it's showing an error as : invalid syntax

It's showing as invalid syntax because it has the wrong brackets, so you could do
ast.literal_eval(a.replace("[","{").replace("]", "}"))
Or alternatively parse the string yourself in a dictionary comprehension
{x.split(":")[0]: x.split(":")[1] for x in a[1:-1].split(",")}
and if as mentioned there are [ or ] elsewhere in your string the following may be more robust
ast.literal_eval("{" + a[1:-1] +"}")

I would simply try
eval(a.replace('[', '{').replace(']', '}'))

To convert to a dict:
Code:
data = '[100:0.345,123:0.34,145:0.86]'
new_data = dict(y.split(':') for y in (x.strip().strip('[').strip(']')
for x in data.split(',')))
print(new_data)
Or if you need numbers not strings:
new_data = dict((map(float, y.split(':'))) for y in (
x.strip().strip('[').strip(']') for x in data.split(',')))
Results:
{'100': '0.345', '123': '0.34', '145': '0.86'}
{145.0: 0.86, 123.0: 0.34, 100.0: 0.345}

Translate brackets to braces, literal_eval.
rebracket = {91: 123, 93: 125}
a = '[100:0.345,123:0.34,145:0.86]'
literal_eval(a.translate(rebracket))

Given a string representation of a dict:
a = '[100:0.345,123:0.34,145:0.86]'
Ignore the containing braces [..], and break up the elements on commas:
a = a[1:-1].split(",")
For each element, separate the key and value:
d1 = [x.split(":") for x in a]
Reconstitute the parsed data as a dict:
d2 = { int(k[0]) : float(k[1]) for k in d1}

Related

evaluating an expression using values

I am trying to evaluate the following expression (exp). I'm trying to replace var[i] in exp by values[i] and evaluate the expression and would really appreciate some help. I have tried re.subs and replace however it isn't working.
exp='((2*3.14)/(550*12))*((R*F*L)/t)'
var=['R','F','L','t']
values=[1202,10.12,15.63,60]
It should output: ((23.14)/(55012))((120210.12*15.63)/60)= 3.016
You can use a f-string with named values and format it with a dict
exp = "((2*3.14)/(550*12))*(({R}*{F}*{L})/{t})"
var = ["R", "F", "L", "t"]
values = [1202, 10.12, 15.63, 60]
valdict = dict(zip(var, values))
print(valdict)
filled = exp.format(**valdict)
print(filled)
print(eval(filled))
Which produces
{'R': 1202, 'F': 10.12, 'L': 15.63, 't': 60}
((2*3.14)/(550*12))*((1202*10.12*15.63)/60)
3.0151464826666667
Of course if you are the one building the var/values, you should skip the two lists and build the valdict directly, which makes it a lot more readable.
And if you want to print only three decimal digits use
print(f"{eval(filled):.3f}")
Which prints 3.015 and not 3.016 as you request, but it should be the right value.
Cheers!
I assume the value for each var is at the same index in the values list
You can just replace a variable with the corresponding value in values list to the exp string
exp='((2*3.14)/(550*12))*((R*F*L)/t)'
var=['R','F','L','t']
values=[1202,10.12,15.63,60]
for i,j in zip(var, values):
exp = exp.replace(i, str(j))
Output:
>>exp
'((2*3.14)/(550*12))*((1202*10.12*15.63)/60)'
You can call eval function to evaluate the value.
>>eval(exp)
3.0151464826666667
If you have only such simple variable names (single letter), then the following will be enough:
expr = '((2*3.14)/(550*12))*((R*F*L)/t)'
var = ['R','F','L','t']
values = [1202,10.12,15.63,60]
for n,v in zip(var, values):
expr = expr.replace(n,str(v))
print(f'{expr} = {eval(expr):.5f}')
Set format specifier to the desired one or remove it.
If you have more complex variables names, try using sympy. Example:
import sympy
var = sympy.symbols('R F L t')
values = [1202,10.12,15.63,60]
expr = sympy.sympify('((2*3.14)/(550*12))*((R*F*L)/t)', evaluate=False)
with sympy.evaluate(False):
num_expr = expr.subs(zip(var, values))
print(f'{num_expr} = {num_expr.doit():.5f}')
Can't get rid of printed '*1/', but you can just remove it by hands:
...
print(f'{num_expr} = {num_expr.doit():.5f}'.replace('*1/','/'))

Utility Function to convert dictionary's "key"=value into key:value

I have seen quite a few links but mostly it gives me errors:
ValueError: Parse error: unable to parse:
'hover_data=["Confirmed","Deaths","Recovered"],
animation_frame="Date",color_continuous_scale="Portland",radius=7,
zoom=0,height=700"'
For example I want to convert the following string into a dict:
abc= 'fn=True, lat="Lat", lon="Long", hover_name="Country/Province/State",hover_data=["Confirmed","Deaths","Recovered"], animation_frame="Date",color_continuous_scale="Portland",radius=7, zoom=0,height=700"'
Expected output:
{'fn': True, "lat":"Lat",
"lon":"Long",
"hover_name":"Country/Province/State",
"hover_data":["Confirmed","Deaths","Recovered"],
"animation_frame":"Date",
"color_continuous_scale":"Portland",
"radius":7,
"zoom":0,
"height":700}
I tried to use this reference's code:
import re
keyval_re = re.compile(r'''
\s* # Leading whitespace is ok.
(?P<key>\w+)\s*=\s*( # Search for a key followed by..
(?P<str>"[^"]*"|\'[^\']*\')| # a quoted string; or
(?P<float>\d+\.\d+)| # a float; or
(?P<int>\d+) # an int.
)\s*,?\s* # Handle comma & trailing whitespace.
|(?P<garbage>.+) # Complain if we get anything else!
''', re.VERBOSE)
def handle_keyval(match):
if match.group('garbage'):
raise ValueError("Parse error: unable to parse: %r" %
match.group('garbage'))
key = match.group('key')
if match.group('str') is not None:
return (key, match.group('str')[1:-1]) # strip quotes
elif match.group('float') is not None:
return (key, float(match.group('float')))
elif match.group('int') is not None:
return (key, int(match.group('int')))
elif match.group('list') is not None:
return (key, int(match.group('list')))
elif match.group('bool') is not None:
return (key, int(match.group('bool')))
print(dict(handle_keyval(m) for m in keyval_re.finditer(abc)))
There seems to be an unwanted double-quote character as the last character of your string abc.
If that is removed, the following solution will work nicely:
eval("dict(" + abc + ")")
Output:
{'fn': True,
'lat': 'Lat',
'lon': 'Long',
'hover_name': 'Country/Province/State',
'hover_data': ['Confirmed', 'Deaths', 'Recovered'],
'animation_frame': 'Date',
'color_continuous_scale': 'Portland',
'radius': 7,
'zoom': 0,
'height': 700}
⚠️ DON'T USE EVAL.
import re, ast
test_string = 'fn=True, lat="Lat", lon="Long", hover_name="Country/Province/State",hover_data=["Confirmed","Deaths","Recovered"], animation_frame="Date",color_continuous_scale="Portland",radius=7, zoom=0,height=700'
items = re.split(r', |,(?=\w)', test_string)
d = {
key: ast.literal_eval(val)
for item in items
for key, val in [re.split(r'=|\s*=\s*', item)]
}
print(d)
I used a very simple method. Just splitted the string on , and then plain dict comprehension. I've also used ast.literal_eval() to convert strings into their respective keywords and data types.

How to replace text between parentheses in Python?

I have a dictionary containing the following key-value pairs: d={'Alice':'x','Bob':'y','Chloe':'z'}
I want to replace the lower case variables(values) by the constants(keys) in any given string.
For example, if my string is:
A(x)B(y)C(x,z)
how do I replace the characters in order to get a resultant string of :
A(Alice)B(Bob)C(Alice,Chloe)
Should I use regular expressions?
re.sub() solution with replacement function:
import re
d = {'Alice':'x','Bob':'y','Chloe':'z'}
flipped = dict(zip(d.values(), d.keys()))
s = 'A(x)B(y)C(x,z)'
result = re.sub(r'\([^()]+\)', lambda m: '({})'.format(','.join(flipped.get(k,'')
for k in m.group().strip('()').split(','))), s)
print(result)
The output:
A(Alice)B(Bob)C(Alice,Chloe)
Extended version:
import re
def repl(m):
val = m.group().strip('()')
d = {'Alice':'x','Bob':'y','Chloe':'z'}
flipped = dict(zip(d.values(), d.keys()))
if ',' in val:
return '({})'.format(','.join(flipped.get(k,'') for k in val.split(',')))
else:
return '({})'.format(flipped.get(val,''))
s = 'A(x)B(y)C(x,z)'
result = re.sub(r'\([^()]+\)', repl, s)
print(result)
Bonus approach for particular input case A(x)B(y)C(Alice,z):
...
s = 'A(x)B(y)C(Alice,z)'
result = re.sub(r'\([^()]+\)', lambda m: '({})'.format(','.join(flipped.get(k,'') or k
for k in m.group().strip('()').split(','))), s)
print(result)
I assume you want to replace the values in a string with the respective keys of the dictionary. If my assumption is correct you can try this without using regex.
First the swap the keys and values using dictionary comprehension.
my_dict = {'Alice':'x','Bob':'y','Chloe':'z'}
my_dict = { y:x for x,y in my_dict.iteritems()}
Then using list_comprehension, you replace the values
str_ = 'A(x)B(y)C(x,z)'
output = ''.join([i if i not in my_dict.keys() else my_dict[i] for i in str_])
Hope this is what you need ;)
Code
import re
d={'Alice':'x','Bob':'y','Chloe':'z'}
keys = d.keys()
values = d.values()
s = "A(x)B(y)C(x,z)"
for i in range(0, len(d.keys())):
rx = r"" + re.escape(values[i])
s = re.sub(rx, keys[i], s)
print s
Output
A(Alice)B(Bob)C(Alice,Chloe)
Also you could use the replace method in python like this:
d={'x':'Alice','y':'Bob','z':'Chloe'}
str = "A(x)B(y)C(x,z)"
for key in d:
str = str.replace(key,d[key])
print (str)
But yeah you should swipe your dictionary values like Kishore suggested.
This is the way that I would do it:
import re
def sub_args(text, tosub):
ops = '|'.join(tosub.keys())
for argstr, _ in re.findall(r'(\(([%s]+?,?)+\))' % ops, text):
args = argstr[1:-1].split(',')
args = [tosub[a] for a in args]
subbed = '(%s)' % ','.join(map(str, args))
text = re.sub(re.escape(argstr), subbed, text)
return text
text = 'A(x)B(y)C(x,z)'
tosub = {
'x': 'Alice',
'y': 'Bob',
'z': 'Chloe'
}
print(sub_args(text, tosub))
Basically you just use the regex pattern to find all of the argument groups and substitute in the proper values--the nice thing about this approach is that you don't have to worry about subbing where you don't want to (for example, if you had a string like 'Fn(F,n)'). You can also have multi-character keys, like 'F(arg1,arg2)'.

Single integer to multiple integer translation in Python

I'm trying to translate a single integer input to a multiple integer output, and am currently using the transtab function. For instance,
intab3 = "abcdefg"
outtab3 = "ABCDEFG"
trantab3 = maketrans(intab3, outtab3)
is the most basic version of what I'm doing. What I'd like to be able to do is have the input be a single letter and the output be multiple letters. So something like:
intab4 = "abc"
outtab = "yes,no,maybe"
but commas and quotation marks don't work.
It keeps saying :
ValueError: maketrans arguments must have same length
Is there a better function I should be using? Thanks,
You can use a dict here:
>>> dic = {"a":"yes", "b":"no", "c":"maybe"}
>>> strs = "abcd"
>>> "".join(dic.get(x,x) for x in strs)
'yesnomaybed'
In python3, the str.translate method was improved so this just works.
>>> intab4 = "abc"
>>> outtab = "yes,no,maybe"
>>> d = {ord(k): v for k, v in zip(intab4, outtab.split(','))}
>>> print(d)
{97: 'yes', 98: 'no', 99: 'maybe'}
>>> 'abcdefg'.translate(d)
'yesnomaybedefg'

how can I convert a char to variable?

In detail:
I need this formula to work.
string = str(z)+":1.0 "+str(z+1)+":0.0" where z is a variable with a value.
will I be able to input this formula into a dictionary value with a specific key. Like
dicto={'A': 'str(z)+":1.0 "+str(z+1)+":0.0"'}
so that when i see a key value 'A' I should be able to use that formula in the dictionary
As I read your question, you wanted something like this:
dicto = {'A': lambda x: "{0!s}:1.0 {1!s}:0.0".format(x, x + 1)}
dicto['A'](2) # '2:1.0 3:0.0'
Use lambda function:
d = { 'A': lambda z: str(z)+":1.0 "+str(z+1)+":0.0" }
d['A'](5)
# returns: '5:1.0 6:0.0'

Categories