python – character replacement combinations with a list

Question:

I’m trying to create a list of words that contains all possible combinations of character replacement by changing multiple characters with their respective lists. The input is also a list of keywords. Example:

keywords=["magic", "mate"]

aoptions = ["a", "4", "@"]
boptions = ["b", "8"]
eoptions = ["e", "3"]
goptions = ["g", "9"]
ioptions = ["i", "1"]
loptions = ["l", "1"]
ooptions = ["o", "0"]
soptions = ["s", "5", "$"]
toptions = ["t", "7"]
zoptions = ["z", "2"]

The desired result would be a list like this:

['magic', 'mag1c', 'ma9ic', 'ma91c'...'m@t3', 'm@73']

I’ve only been able to create a solution for one keyword at a time and substituting a single character with another single character. The algorithm was found here String Replacement Combinations. It looks like this

from itertools import product

def filler(word, from_char, to_char):
   options = [(c,) if c != from_char else (from_char, to_char) for c in word]
   return (''.join(o) for o in product(*options))

Which results in:

>>> filler("magic", "a", "4")
<generator object <genexpr> at 0x8fa798c>
>>> list(filler("magic", "a", "4"))
['magic', 'm4gic']

It isn’t particularly important, but the list of keywords will be read from a .txt file with a single keyword on each line and the resulting list of combinations will be written to a .txt file with a single word on each line. I’ve been trying to create different iterative loops and modifying the itertools.product example for days without any luck. Any and all help is much appreciated.

UPDATE:
Updating my filler function based on #zefciu advice I was able to solve using this method:

from itertools import product

def filler(word):
   combos = [(c,) if c not in options else options[c] for c in word]
   return (''.join(o) for o in product(*combos))

options = {
  'A': ['A', '4', '@'],
  'B': ['B', '8',],
  'E': ["E", "3"],
  'G': ["G", "9"],
  'I': ["I", "1", "!"],
  'L': ["L", "1"],
  'O': ["O", "0"],
  'S': ["S", "5", "$"],
  'T': ["T", "7"],
  'Z': ["Z", "2"]}

with open('CustomList.txt', 'r') as f:
   startlist = f.readlines()
startlist = [x.strip() for x in startlist]
startlist = [element.upper() for element in startlist]

filid= open('WordList.txt', 'w+')
for word in startlist:
   temp_list=list(filler(word))
for newword in temp_list:
   print >> filid, newword
filid.close()
Asked By: jca12

||

Answers:

First of all, don’t store this kind of data in separate variables. This data asks for a dict like this:

options = {
    'a': ['a', '4', '@'],
    'y': ['y', 'ყ'],
}

This way you only need to modify the function a little. Instead of checking identity with a single value, check if it is in your dictionary:

[c,] if c not in options else options[c]
Answered By: zefciu

There might be other approaches but Since you started off with one I will proceed with that.

def filler_list(word_list, from_char, to_char):
   return_list=[]
   for word in word_list:
      return_list=return_list+list(filler(word,from_char,to_char))
   return return_list

Then just loop over each character i.e, start with list(filler(“magic”, “a”, “4”))
and pass it’s output to filler_list as input and character changed and so on this will give you your answer may be with some repeats but they can be removed if not necessary,Hope this helps Cheers!

Answered By: gowtham

Iteration + recursion.

Considering just the one word, ‘magic’.

Look at each letter in turn. m-a-g-i-c. (iteration). Is it in the list of letters that can be substituted? If so, do the substitution and retain the substituted form in the results list. Now, if that is the final letter in ‘magic’ continue the iteration, otherwise start a recursive descent, keeping that choice of letter in that position but entertaining all possible choices for the next substitutable letter.

And so on to completion.

Answered By: Bill Bell