Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Separating strings with lists

I was trying to find a way to separate strings in a project of my called 'Chemistry Calculator'. This project takes strings from an input() and compare it in a list:

    substance1 = input('Substance 1: ')
    substance2 = input('Substance 2: ')
    elements = ['f','o','cl','br','i','s','c']

    def affinity_table(element1:str,element2:str,table:list) -> str:
        s = element1.lower()
        r = element2.lower()
        if s in table and r in table:

            if table.index(s) < table.index(r):
                print(s," will chage with ", r)

            else:
                print(s," won't change with ", r)

        else:
            print("Those substances are't in the list")

This code above works well.

So I wanted to have it working with hole substances and not just the element. To do this I need to separate the substance in to parts:

  • the cations parts
  • the anions parts.

Then I need to compare them with the list. I noticed that the contains() function showed exactly what I wanted, but only with one comparison.

My question came from: Is there a way of using the contains() function with more than one string and then separate the string in to where the similarity is found.

Something similar to this:

a = 'NaCO3' #First input.
b = 'KCO3'  #Second input.
list = ['Na','K']   #The list.

# Way of separating the values with the list.
     # ^ my objective.
a1 = 'Na' #Separation with a.
a2 = 'CO3' #The rest of a.
b1 = 'K' #The rest of b.
b2 = 'CO3' #The rest of b.
     # ^ expected outputs from the separation.
if table.index(a1) < table.index(a2):
      print(a1,' will change with ', b1, 'and become', a1 + b2)

else:
      print(a1," won't change with ", b1, 'and will stay normal')
            # ^ the list index comparison from the 1st code.

#After the solution, here are the results: The Results

like image 605
Locochoco Avatar asked Aug 28 '26 19:08

Locochoco


1 Answers

Disclaimer

Just to be clear: for the constrained scope of what you are doing this solution might be applicable. If you want to parse any chemical compound (and those can look quite complicated) you need a full fledged parser, not the toy regex solution I came up with.


Here's an idea:

Dynamically build a regex with elements from your list as alternating matching groups. (re.split keeps groups when splitting.)

>>> import re
>>> lst = ['Na', 'K']
>>> regex = '|'.join('({})'.format(a) for a in lst)
>>> regex
>>> '(Na)|(K)'

Apply the regex...

>>> re.split(regex, 'NaCO3')
>>> ['', 'Na', None, 'CO3']
>>> re.split(regex, 'KCO3')
>>> ['', None, 'K', 'CO3']

... and filter out falsy values (None, '')

>>> list(filter(None, re.split(regex, 'NaCO3')))
>>> ['Na', 'CO3']
>>> list(filter(None, re.split(regex, 'KCO3')))
>>> ['K', 'CO3']

You can assign to those values with extended iterable unpacking:

>>> b1, b2, *unexpected_rest = filter(None, re.split(regex, 'KCO3'))
>>> b1
>>> 'K'
>>> b2
>>> 'CO3'

If you want to bias the split in favor of longer matches, sort lst in descending order first.

Not good:

>>> lst = ['N', 'Na', 'CO3']
>>> regex = '|'.join('({})'.format(a) for a in lst)
>>> list(filter(None, re.split(regex, 'NaCO3')))
>>> ['N', 'a', 'CO3']

Better:

>>> lst = ['N', 'Na', 'CO3']
>>> lst = sorted(lst, key=len, reverse=True)
>>> regex = '|'.join('({})'.format(a) for a in lst)
>>> list(filter(None, re.split(regex, 'NaCO3')))
>>> ['Na', 'CO3']

Let me know if that works for you.

like image 191
timgeb Avatar answered Aug 31 '26 07:08

timgeb



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!