Can you use two dictionary values to replace text in Python?
Question:
What should I do if the output of my code has changed only two texts
dict_data = [{'test 2': '333','m': 'mm'}, {'sss': 'n','help': '5'}]
texts = ["test 2 text m and text", "text help sss"]
for text in texts:
for dict_data_a in dict_data:
out = " ".join(dict_data_a.get(ele, ele) for ele in text.split())
print(out)
Something that waits for the output
333 2 text mm and text
text 5 n
Answers:
Can you use two dictionary values to replace text in Python? Yes
You could use str.replace
for text in texts:
for d in dict_data:
for k,v in d.items():
text = text.replace(k, v)
print(text)
Output:
333 text mm and text
text 5 n
1st problem
You have a double loop. So you have 4 combinations: 1st text with 1st dict, 1st text with 2nd dict, 2nd text with 1st dict, …
2 of them are the ones you wanted. 2 others are.
Here, I surmise that you wanted to replace words of the 1st text with 1st dict, and 2nd text with 2nd dict.
So you need 1 loop. But iterating both list (text and dict) at the same time.
Simple (to understand) version
dict_data = [{'test 2': '333','m': 'mm'}, {'sss': 'n','help': '5'}]
texts = ["test 2 text m and text", "text help sss"]
for i in range(len(texts)):
text=texts[i]
dict_data_a=dict_data[i]
out = " ".join(dict_data_a.get(ele, ele) for ele in text.split())
print(out)
Or, more pythonesque one
dict_data = [{'test 2': '333','m': 'mm'}, {'sss': 'n','help': '5'}]
texts = ["test 2 text m and text", "text help sss"]
for text,dict_data_a in zip(texts, dict_data):
out = " ".join(dict_data_a.get(ele, ele) for ele in text.split())
print(out)
2nd problem
You are basing your replacement on words. Yet you have one replacement that cannot fit a word, since it is made of 2 words (test 2).
You can’t not just replace one substring by another with replace
, because then you would be, for example, replacing all m
by mm
, even those that are just letters in a word. For example looming
would be replaced by loomming
.
So for that, there are certainly many solutions, but the easiest one would be using regex, I think.
import re
dict_data = [{'test 2': '333','m': 'mm'}, {'sss': 'n','help': '5'}]
texts = ["test 2 text m and text", "text help sss"]
for text,dict_data_a in zip(texts, dict_data):
out = text
for org,rep in dict_data_a.items():
out = re.sub(r'(?<!w)'+org+'(?!w)', rep, out)
print(out)
Here we replace each key by its associated value, but using a reg ex, consisting of the key (org), prefixed by a "look-behind" (?<!z)
that make the patter matches only occurrence of org that are not preceded by a letter (w
). And suffixed by a "look-ahred" (?!w)
that also forces the pattern to match only occurrence of org that are not followed immediately by a letter.
So "m one" will be replace by "mm one". "two m three" by "two mm three", "four m" by "four mm". But "many or some boom" won’t turn into "mmany or somme boomm"
Here is my approach: It is easier to deal with one dictionary, than multiple ones, so I use collections.ChainMap
to turn a list of dictionary (dict_data
) into a single dictionary (replacements
). Then it is a matter of writing a simple function to transform all the old text to new ones (replace_all
).
import collections
def replace_all(text, lookup_dict):
for old_value, new_value in lookup_dict.items():
text = text.replace(old_value, new_value)
return text
dict_data = [{'test 2': '333','m': 'mm'}, {'sss': 'n','help': '5'}]
texts = ["test 2 text m and text", "text help sss"]
replacements = collections.ChainMap(*dict_data)
new_texts = [replace_all(text, replacements) for text in texts]
print("n".join(new_texts))
Notes
replacements
behave like a single, combined dictionary
- The
print
statement is to produce the output
Output
333 text mm and text
text 5 n
What should I do if the output of my code has changed only two texts
dict_data = [{'test 2': '333','m': 'mm'}, {'sss': 'n','help': '5'}]
texts = ["test 2 text m and text", "text help sss"]
for text in texts:
for dict_data_a in dict_data:
out = " ".join(dict_data_a.get(ele, ele) for ele in text.split())
print(out)
Something that waits for the output
333 2 text mm and text
text 5 n
Can you use two dictionary values to replace text in Python? Yes
You could use str.replace
for text in texts:
for d in dict_data:
for k,v in d.items():
text = text.replace(k, v)
print(text)
Output:
333 text mm and text
text 5 n
1st problem
You have a double loop. So you have 4 combinations: 1st text with 1st dict, 1st text with 2nd dict, 2nd text with 1st dict, …
2 of them are the ones you wanted. 2 others are.
Here, I surmise that you wanted to replace words of the 1st text with 1st dict, and 2nd text with 2nd dict.
So you need 1 loop. But iterating both list (text and dict) at the same time.
Simple (to understand) version
dict_data = [{'test 2': '333','m': 'mm'}, {'sss': 'n','help': '5'}]
texts = ["test 2 text m and text", "text help sss"]
for i in range(len(texts)):
text=texts[i]
dict_data_a=dict_data[i]
out = " ".join(dict_data_a.get(ele, ele) for ele in text.split())
print(out)
Or, more pythonesque one
dict_data = [{'test 2': '333','m': 'mm'}, {'sss': 'n','help': '5'}]
texts = ["test 2 text m and text", "text help sss"]
for text,dict_data_a in zip(texts, dict_data):
out = " ".join(dict_data_a.get(ele, ele) for ele in text.split())
print(out)
2nd problem
You are basing your replacement on words. Yet you have one replacement that cannot fit a word, since it is made of 2 words (test 2).
You can’t not just replace one substring by another with replace
, because then you would be, for example, replacing all m
by mm
, even those that are just letters in a word. For example looming
would be replaced by loomming
.
So for that, there are certainly many solutions, but the easiest one would be using regex, I think.
import re
dict_data = [{'test 2': '333','m': 'mm'}, {'sss': 'n','help': '5'}]
texts = ["test 2 text m and text", "text help sss"]
for text,dict_data_a in zip(texts, dict_data):
out = text
for org,rep in dict_data_a.items():
out = re.sub(r'(?<!w)'+org+'(?!w)', rep, out)
print(out)
Here we replace each key by its associated value, but using a reg ex, consisting of the key (org), prefixed by a "look-behind" (?<!z)
that make the patter matches only occurrence of org that are not preceded by a letter (w
). And suffixed by a "look-ahred" (?!w)
that also forces the pattern to match only occurrence of org that are not followed immediately by a letter.
So "m one" will be replace by "mm one". "two m three" by "two mm three", "four m" by "four mm". But "many or some boom" won’t turn into "mmany or somme boomm"
Here is my approach: It is easier to deal with one dictionary, than multiple ones, so I use collections.ChainMap
to turn a list of dictionary (dict_data
) into a single dictionary (replacements
). Then it is a matter of writing a simple function to transform all the old text to new ones (replace_all
).
import collections
def replace_all(text, lookup_dict):
for old_value, new_value in lookup_dict.items():
text = text.replace(old_value, new_value)
return text
dict_data = [{'test 2': '333','m': 'mm'}, {'sss': 'n','help': '5'}]
texts = ["test 2 text m and text", "text help sss"]
replacements = collections.ChainMap(*dict_data)
new_texts = [replace_all(text, replacements) for text in texts]
print("n".join(new_texts))
Notes
replacements
behave like a single, combined dictionary- The
print
statement is to produce the output
Output
333 text mm and text
text 5 n