I've created a script to print all periodic table symbolic permutations of a string. As seen in the opening credits of each episode of Breaking Bad:

Breaking Bad intro

© 2010-2022 AMC Networks Entertainment LLC.

Aaron Paul contains the symbol for Argon (Ar) which is highlighted green, and overwrites the casing on the original (ar).

I have made this just for fun, and given the script the following restrictions/scope (which are inherited from the TV show):

  • Text replaced by an element's symbol must:
    • Be green
    • Have the casing of the element as seen in the periodic table (H,Ar)
  • There is only one replacement per permutation A[Ar]on and Aaro[N] are seperate. I.e, don't have any more than 1 symbol in the string at a time
  • Case is ignored for matches (h & H should both be replaced with a green H)

And an extra that's not in the show:

  • Print the element's name afterwards
    • Example output: A[Ar]on - Argon

This is what I have created on a basic level (no user input or text entering loops).

import colorama as col
import re

data = {'H': 'Hydrogen', 'He': 'Helium', 'Li': 'Lithium', 'Be': 'Beryllium', 'B': 'Boron', 'C': 'Carbon', 'N': 'Nitrogen', 'O': 'Oxygen', 'F': 'Fluorine', 'Ne': 'Neon', 'Na': 'Sodium', 'Mg': 'Magnesium', 'Al': 'Aluminium', 'Si': 'Silicon', 'P': 'Phosphorus', 'S': 'Sulphur', 'Cl': 'Chlorine', 'Ar': 'Argon', 'K': 'Potassium', 'Ca': 'Calcium', 'Sc': 'Scandium', 'Ti': 'Titanium', 'V': 'Vanadium', 'Cr': 'Chromium', 'Mn': 'Manganese', 'Fe': 'Iron', 'Co': 'Cobalt', 'Ni': 'Nickel', 'Cu': 'Copper', 'Zn': 'Zinc', 'Ga': 'Gallium', 'Ge': 'Germanium', 'As': 'Arsenic', 'Se': 'Selenium', 'Br': 'Bromine', 'Kr': 'Krypton', 'Rb': 'Rubidium', 'Sr': 'Strontium', 'Y': 'Yttrium', 'Zr': 'Zirconium', 'Nb': 'Niobium', 'Mo': 'Molybdenum', 'Tc': 'Technetium', 'Ru': 'Ruthenium', 'Rh': 'Rhodium', 'Pd': 'Palladium', 'Ag': 'Silver', 'Cd': 'Cadmium', 'In': 'Indium', 'Sn': 'Tin', 'Sb': 'Antimony', 'Te': 'Tellurium', 'I': 'Iodine', 'Xe': 'Xenon', 'Cs': 'Caesium', 'Ba': 'Barium', 'La': 'Lanthanum', 'Ce': 'Cerium', 'Pr': 'Praseodymium', 'Nd': 'Neodymium', 'Pm': 'Promethium',
        'Sm': 'Samarium', 'Eu': 'Europium', 'Gd': 'Gadolinium', 'Tb': 'Terbium', 'Dy': 'Dysprosium', 'Ho': 'Holmium', 'Er': 'Erbium', 'Tm': 'Thulium', 'Yb': 'Ytterbium', 'Lu': 'Lutetium', 'Hf': 'Hafnium', 'Ta': 'Tantalum', 'W': 'Tungsten', 'Re': 'Rhenium', 'Os': 'Osmium', 'Ir': 'Iridium', 'Pt': 'Platinum', 'Au': 'Gold', 'Hg': 'Mercury', 'Tl': 'Thallium', 'Pb': 'Lead', 'Bi': 'Bismuth', 'Po': 'Polonium', 'At': 'Astatine', 'Rn': 'Radon', 'Fr': 'Francium', 'Ra': 'Radium', 'Ac': 'Actinium', 'Th': 'Thorium', 'Pa': 'Protactinium', 'U': 'Uranium', 'Np': 'Neptunium', 'Pu': 'Plutonium', 'Am': 'Americium', 'Cm': 'Curium', 'Bk': 'Berkelium', 'Cf': 'Californium', 'Es': 'Einsteinium', 'Fm': 'Fermium', 'Md': 'Mendelevium', 'No': 'Nobelium', 'Lr': 'Lawrencium', 'Rf': 'Rutherfordium', 'Db': 'Dubnium', 'Sg': 'Seaborgium', 'Bh': 'Bohrium', 'Hs': 'Hassium', 'Mt': 'Meitnerium', 'Ds': 'Darmstadtium', 'Rg': 'Roentgenium', 'Cn': 'Copernicium', 'Nh': 'Nihonium', 'Fl': 'Flerovium', 'Mc': 'Moscovium', 'Lv': 'Livermorium', 'Ts': 'Tennessine', 'Og': 'Oganesson'}

user_input = "Aaron"

for symbol, name in data.items():
    for i in [x.start() for x in re.finditer(symbol, user_input, re.IGNORECASE)]:

        temp = list(user_input)
        temp[i:i+len(symbol)] = col.Fore.GREEN + symbol + col.Fore.RESET

        # [V]ince - Vanadium
        print(f"{''.join(temp)} - {name}")


  1. How could I improve on the cleanliness and efficiency of the text substitution? As I'm literally just putting it in a list and replacing with re index matches:
temp = list(user_input)
temp[i:i+len(symbol)] = col.Fore.GREEN + symbol + col.Fore.RESET
  1. And what other general constructive comments can be made about my attempt?

1 Answer 1


Define a proper constant for the elements. The name data is both vague and improperly formatted for a constant. One option would be something like ELEMENTS.

Put your code in functions. Even for small scripts.

Parameterize the script. Take the name from the command line.

Don't use an extra layer of iteration as a workaround for a simple assignment. Drop the innermost iteration and just use this instead: i = x.start(). [And after some further edits, you might not need i at all.]

You don't need to listify the name. Python strings are already sequences, so converting from string to list doesn't help in this situation. Just grab the substrings you need and glue everything together.

Don't print in the function responsible for core computation. The function to compute the breaking-bad names already has a primary job. Leave the printing to a different part of the program. Among other benefits, this type of discipline aids testing.

Optionally, shift some of the calculation out of the innermost loop. Values derived from the symbol can be computed at the level of the outer loop, and making this change has some benefits in terms of readability and to instill good habits. In a different context -- where performance is a consideration -- you don't want to repeat calculations than can be done once at a higher level.

import colorama as col
import sys
import re

ELEMENTS = {...}

def main(args):
    name = args[0]
    for display, element in breaking_bad_names(name):
        print(display, element)

def breaking_bad_names(name):
    for symbol, element in ELEMENTS.items():
        green_sym = col.Fore.GREEN + symbol + col.Fore.RESET
        for m in re.finditer(symbol, name, re.IGNORECASE):
            display = name[0 : m.start()] + green_sym + name[m.end() :]
            yield (display, element)

if __name__ == '__main__':

  • 2
    \$\begingroup\$ @RichardNeumann args == sys.argv[1:] but, yes, argparse is useful to know about in general. Not sure I would bother with it in the case at hand. \$\endgroup\$
    – FMc
    Apr 21, 2022 at 20:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.