2
\$\begingroup\$

I wrote the following Python program to take 1 word from the user and return certain characters replaced by numbers. Given a dictionary, the program should print all possible combinations of the same word with various characters substituted:

import sys def main(argv): if len(argv) != 2: print(f"USAGE: {argv[0]} [word]") return replacements = {} # Populate the known replacements replacements['i'] = ['1'] replacements['e'] = ['3'] replacements['m'] = ['/v\\'] replacements['a'] = ['4'] replacements['r'] = ['2'] replacements['o'] = ['0'] print_possibilities(argv[1], replacements, []) def print_possibilities(s: str, replacements: dict, dupes: list): ctr = 0 tally = 0 for c in s: if c.lower() in replacements: tally += 1 if tally == 0: return for c in s: if c.lower() in replacements: for r in replacements[c.lower()]: tmp = list(s) tmp[ctr] = r as_str = ''.join(tmp) if as_str in dupes: continue print(as_str) dupes.append(as_str) print_possibilities(as_str, replacements, dupes) ctr += 1 return if __name__ == '__main__': main(sys.argv) 

I've tested with python3 replace_word.py mondoman and I get this output:

/v\ondoman /v\0ndoman /v\0nd0man /v\0nd0/v\an /v\0nd0/v\4n /v\0nd0m4n /v\0ndo/v\an /v\0ndo/v\4n /v\0ndom4n /v\ond0man /v\ond0/v\an /v\ond0/v\4n /v\ond0m4n /v\ondo/v\an /v\ondo/v\4n /v\ondom4n m0ndoman m0nd0man m0nd0/v\an m0nd0/v\4n m0nd0m4n m0ndo/v\an m0ndo/v\4n m0ndom4n mond0man mond0/v\an mond0/v\4n mond0m4n mondo/v\an mondo/v\4n mondom4n 

I feel that this could be improved to be more performant and more idiomatic and would like a code review.

\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

Think about the problem as a repeated Cartesian product. It allows us to use the very useful product function from the itertools module:

>>> list(product('ab', 'AB', 'xy')) [('a', 'A', 'x'), ('a', 'A', 'y'), ('a', 'B', 'x'), ('a', 'B', 'y'), ('b', 'A', 'x'), ('b', 'A', 'y'), ('b', 'B', 'x'), ('b', 'B', 'y')] 

So your problem can be solved by replacing each character in the string with its possible replacemnets and running product on the result:

import itertools import sys def alternatives(ch, repls): key = ch.lower() if key in repls: return [ch, repls[key]] return [ch] def main(argv): if len(argv) != 2: print(f"USAGE: {argv[0]} [word]") return # Populate the known replacements replacements = {'a' : '4', 'e' : '3', 'i' : '1', 'm' : '/v\\', 'o' : '0', 'r' : '2'} s = [alternatives(ch, replacements) for ch in argv[1]] for it in itertools.product(*s): print(''.join(it)) if __name__ == '__main__': main(sys.argv) 

Note that this version is not exactly identical to your original. If you enter "mondoman" the program will output "mondoman" as one combination.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ I was just looking at this question thinking "oo, this looks like a good place for itertools" but you beat me to the answer. :D \$\endgroup\$ Commented Dec 24, 2019 at 15:13

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.