Python split string and add characters wherever parse occurs

Viewed 1331

Note: I'm a chemist, and have very little experience coding

I would like to split every string in a list by a particular delimiter, then add each parsed string to a new list. It would be very helpful for me to keep track of where the splits occurred by adding a character to the end of one string and the beginning of the next string for every parse. For example,

TestString = 'AAABA'
parsed = TestString.split('B')
print(parsed)

Will output:

['AAA','A']

I would like the output to be:

['AAAx', 'xA']

I'm looking for a solution that will likewise work for strings that contain only my delimiter. My end goal will involve parsing a large list of strings (1-10 million strings per list with a string length varying from 1 to 1000). Another example:

TestList = ['A', 'B', 'AB', 'BA', 'BBB','ABA', 'AAA']
Parsed = []
for i in range(len(TestList)):
     parsed = TestList[i].split('B')
     Parsed.extend(parsed)
print(Parsed)

Will output:

['A', '', '', 'A', '', '', 'A', '', '', '', '', 'A', 'A', 'AAA']

I would like the output to look like:

['A','x', 'x', 'Ax', 'x', 'x', 'xA', 'x', 'xx', 'xx', 'x', 'Ax', 'xA', 'AAA']

'B' is split into ['x' and 'x']; 'BB' is split into ['x', 'xx', 'x'], etc.

Are there any simple ways to accomplish this? I've looked into regex a little bit was having trouble applying (or compiling) my patterns to the variable string lengths in my dataset. I've searched around on stackoverflow and did some online searches, but couldn't find anything supremely useful. I know I can add characters to the beginning and ends of strings, but any solution should never add a character to an unparsed string. I've considered trying to add a bunch of 'if' conditional statements to try and cover all my possibilities, but this seems like it would be a pain and would prefer a simpler soluton if its out there.

EDIT: In response to the comments, I'm really trying to take my strings and "cut the 'B' units in half" and add a character that represents half of a 'B' unit.

In the image below, the black lines represent a string of 'A's. Left of the arrow: a single string with only the 'B' units explicitly drawn. Right of the arrow: after parsing on 'B', 3 strings result. Each 'B' unit is cut in half and replaced by an 'x'. Description

Thinking about the example 'BBB' stepwise, the first parse will result in ['x', 'xBB']. The second step results in ['x', 'xx', 'xB']. The final step results in ['x', 'xx', 'xx', 'x']

After parsing, there will be no 'B' units remaining. This is a problem related to chemistry, and 'xx' and 'B' are in fact distinct entities (even though 'x' is "half of a 'B' unit". Its also worth noting that parsing 'AAABAAA' into ['AAAx', 'xAAA'] is important because neither of these strings is (should be) equivalent to the string 'AAA', which never contained a 'B' unit in the first place.

3 Answers

Try this:

  1. First replace 'B' with 'x-x'

  2. Split the string with separator '-'.

     string = 'AAABA'
     string = string.replace('B', 'x-x')
     print( string.split('-') )
    
     OUT: ['AAAx', 'xA']`
    
string = 'AABBA'
string = string.replace('B', 'x-x')
print( string.split('-') )

out: ['AAx', 'xx', 'xA']

Here is a custom split function which I believes does what you want. Whenever there is a split, include a symbol 'x' to mark that that occurred:

def my_split(s, delim, split_sym='x'):
    output = []
    substring = ''
    for c in s:
        if c == delim:
            output.append(substring + split_sym)
            substring = split_sym
        else:
            substring += c
    output.append(substring)
    return output

Note that a split_sym of '' would be equivalent to the normal split (not sure if this implementation is the same, but I believe the output should match).

Testing against your cases:

tests = ['A', 'B', 'AB', 'BA', 'BBB','ABA', 'AAA']
parsed = []
for s in tests:
    parsed.extend(my_split(s, 'B'))
print(parsed)

Output:

['A', 'x', 'x', 'Ax', 'x', 'x', 'xA', 'x', 'xx', 'xx', 'x', 'Ax', 'xA', 'AAA']

You can use recursion with a generator:

def get_xs(s):
  if (ind:=next((i for i, a in enumerate(s) if a == 'B'), None)) is not None:
     k = s[:ind]+'x'+s[ind+1:]
     yield k[:(x:=[j for j, l in enumerate(k) if l == 'x'][-1])+1]
     yield from get_xs(k[x:])
  elif s:
     yield s

testlist = ['A', 'B', 'AB', 'BA', 'BBB','ABA', 'AAA']
new_list = [i for b in testlist for i in get_xs(b)]
print(new_list)

Output:

['A', 'x', 'x', 'Ax', 'x', 'x', 'xA', 'x', 'xx', 'xx', 'x', 'Ax', 'xA', 'AAA']
Related