This answer is very similar to Brian's answer, but a little bit sanitized and the output has no duplicates:
words = ["Words", "Shirt", "Blouse", "Sweater"]
md = {"e": "q,w", "a": "z"}
md = {k: v.split(',') for k, v in md.items()}
newwords = []
for word in words:
newwords.append(word)
for c in md:
occ = word.count(c)
pos = 0
for _ in range(occ):
pos = word.find(c, pos)
for r in md[c]:
tmp = word[:pos] + r + word[pos+1:]
newwords.append(tmp)
pos += 1
Content of newwords
:
['Words', 'Shirt', 'Blouse', 'Blousq', 'Blousw', 'Sweater', 'Swqater', 'Swwater', 'Sweatqr', 'Sweatwr', 'Swezter']
Prettyprint:
Words
Shirt
Blouse
Blousq
Blousw
Sweater
Swqater
Swwater
Sweatqr
Sweatwr
Swezter
Any errors are a result of the current time. ;)
Update (explanation)
tl;dr
The main idea is to find the occurences of the character in the word one after another. For each occurence we are then replacing it with the replacing-char (again one after another). The replaced word get's added to the output-list.
I will try to explain everything step by step:
words = ["Words", "Shirt", "Blouse", "Sweater"]
md = {"e": "q,w", "a": "z"}
Well. Your basic input. :)
md = {k: v.split(',') for k, v in md.items()}
A simpler way to deal with replacing-dictionary. md
now looks like {"e": ["q", "w"], "a": ["z"]}
. Now we don't have to handle "q,w"
and "z"
differently but the step for replacing is just the same and ignores the fact, that "a"
only got one replace-char.
newwords = []
The new list to store the output in.
for word in words:
newwords.append(word)
We have to do those actions for each word (I assume, the reason is clear). We also append the world directly to our just created output-list (newwords
).
for c in md:
c
as short for character
. So for each character we want to replace (all keys of md
), we do the following stuff.
occ = word.count(c)
occ
for occurrences
(yeah. count
would fit as well :P). word.count(c)
returns the number of occurences of the character/string c
in word
. So "Sweater".count("o") => 0
and "Sweater".count("e") => 2
.
We use this here to know, how often we have to take a look at word
to get all those occurences of c
.
pos = 0
Our startposition to look for c
in word
. Comes into use in the next loop.
for _ in range(occ):
For each occurence. As a continual number has no value for us here, we "discard" it by naming it _
. At this point where c
is in word
. Yet.
pos = word.find(c, pos)
Oh. Look. We found c
. :) word.find(c, pos)
returns the index of the first occurence of c
in word
, starting at pos
. At the beginning, this means from the start of the string => the first occurence of c
. But with this call we already update pos
. This plus the last line (pos += 1
) moves our search-window for the next round to start just behind the previous occurence of c
.
for r in md[c]:
Now you see, why we updated mc
previously: we can easily iterate over it now (a md[c].split(',')
on the old md
would do the job as well). So we are doing the replacement now for each of the replacement-characters.
tmp = word[:pos] + r + word[pos+1:]
The actual replacement. We store it in tmp
(for debug-reasons). word[:pos]
gives us word
up to the (current) occurence of c
(exclusive c
). r
is the replacement. word[pos+1:]
adds the remaining word (again without c
).
newwords.append(tmp)
Our so created new word tmp
now goes into our output-list (newwords
).
pos += 1
The already mentioned adjustment of pos
to "jump over c
".
Additional question from OP: Is there an easy way to dictate how many letters in the string I want to replace [(meaning e.g. multiple at a time)]?
Surely. But I have currently only a vague idea on how to achieve this. I am going to look at it, when I got my sleep. ;)
words = ["Words", "Shirt", "Blouse", "Sweater", "multipleeee"]
md = {"e": "q,w", "a": "z"}
md = {k: v.split(',') for k, v in md.items()}
num = 2 # this is the number of replaces at a time.
newwords = []
for word in words:
newwords.append(word)
for char in md:
for r in md[char]:
pos = multiples = 0
current_word = word
while current_word.find(char, pos) != -1:
pos = current_word.find(char, pos)
current_word = current_word[:pos] + r + current_word[pos+1:]
pos += 1
multiples += 1
if multiples == num:
newwords.append(current_word)
multiples = 0
current_word = word
Content of newwords
:
['Words', 'Shirt', 'Blouse', 'Sweater', 'Swqatqr', 'Swwatwr', 'multipleeee', 'multiplqqee', 'multipleeqq', 'multiplwwee', 'multipleeww']
Prettyprint:
Words
Shirt
Blouse
Sweater
Swqatqr
Swwatwr
multipleeee
multiplqqee
multipleeqq
multiplwwee
multipleeww
I added multipleeee
to demonstrate, how the replacement works: For num = 2
it means the first two occurences are replaced, after them, the next two. So there is no intersection of the replaced parts. If you would want to have something like ['multiplqqee', 'multipleqqe', 'multipleeqq']
, you would have to store the position of the "first" occurence of char
. You can then restore pos
to that position in the if multiples == num:
-block.
If you got further questions, feel free to ask. :)