How to split string at specific character and build different string combinations

Viewed 256

I have some strings in a text file that I want to process. I tried many regex patterns but none of them are working for me.

someone can tell/figure
a/the squeaky wheel gets the grease/oil
accounts for (someone or something)
that's/there's (something/someone) for you

I need the following string combinations:

someone can tell
someone can figure
a squeaky wheel gets the grease
a squeaky wheel gets the oil
the squeaky wheel gets the grease
the squeaky wheel gets the oil
accounts for someone
accounts for something
that's something for you
that's someone for you
there's something for you
there's someone for you
5 Answers

edit: corrected for brackets and " or " which I missed last version

A simple recurrent solution that also works with more than one slash (he/she/it/whatever):

def explode_versions(s):
    match = re.search('^(.*?)(\S+)(?:(?:(?: or )|/)(\S+))+(.*?)$', s)
    
    if match:
        head, *versions, tail = match.groups()
        
        versions[0] = re.sub('^\(', '', versions[0])
        versions[-1] = re.sub('\)$', '', versions[-1])

        return [line for v in versions for line in explode_versions(''.join([head, v, tail]))]
    else:
        return [s]

texts = ["someone can tell/figure",
"a/the squeaky wheel gets the grease/oil",
"accounts for (someone or something)",
"that's/there's (something/someone) for you"]

[explode_versions(text) for text in texts]

result:

[['someone can tell', 'someone can figure'],
 ['a squeaky wheel gets the grease',
  'a squeaky wheel gets the oil',
  'the squeaky wheel gets the grease',
  'the squeaky wheel gets the oil'],
 ['accounts for someone', 'accounts for something'],
 ["that's something for you",
  "that's someone for you",
  "there's something for you",
  "there's someone for you"]]

You can use cartesian product:

from itertools import product
import re

s = 'a/the squeaky wheel gets the grease/oil'

lst = [i.split('/') for i in re.split(r'(\w+[\/\w+]+)', s) if i]
# [['a', 'the'], [' squeaky wheel gets the '], ['grease', 'oil']]

[''.join(i) for i in product(*lst)]

Output:

['a squeaky wheel gets the grease',
 'a squeaky wheel gets the oil',
 'the squeaky wheel gets the grease',
 'the squeaky wheel gets the oil']

it's a bit tricky one, but the main idea is to duplicate the options so far when reaching \ and keep track over 2 of the options, take a look on this:

m_str = ['someone can tell/figure',
'a/the squeaky wheel gets the grease/oil',
'accounts for (someone or something)',
'that\'s/there\'s (something/someone) for you']

lines = [[]]
for line in m_str:
    options = [[]]
    for word in line.split(" "):
        if "/" in word:
            new_options = []
            for option in options:
                new_options.append(option + [word.split("/")[0]])
                new_options.append(option + [word.split("/")[1]])
            options = new_options
            # print(new_options)
                # options = [m_func(options, item) for item in options]

    
        else:
            for option in options:
                option.append(word)
    lines.append(options)
print(lines[1:])

output:

[[['someone', 'can', 'tell'], ['someone', 'can', 'figure']], [['a', 'squeaky', 'wheel', 'gets', 'the', 'grease'], ['a', 'squeaky', 'wheel', 'gets', 'the', 'oil'], ['the', 'squeaky', 'wheel', 'gets', 'the', 'grease'], ['the', 'squeaky', 'wheel', 'gets', 'the', 'oil']], [['accounts', 'for', '(someone', 'or', 'something)']], [["that's", '(something', 'for', 'you'], ["that's", 'someone)', 'for', 'you'], ["there's", '(something', 'for', 'you'], ["there's", 'someone)', 'for', 'you']]]

This solves the question for / and or. I did not consider the brackets, but this should be fairly easy to do so :)

lines = ["someone can tell/figure",
         "a/the squeaky wheel gets the grease/oil",
         "accounts for (someone or something)",
         "that's/there's (something/someone) for you"]

result = []
for line in lines:
    sequences = []
    skip = False
    if "/" in line or "or" in line:
        words = line.split(" ")
        temp_words = []
        for index, word in enumerate(words):
            if skip:
                skip = False
                continue
            else:
                if "/" in word:
                    options = word.split("/")
                    if len(sequences) > 0:
                        temp = []
                        for seq in sequences:
                            for opt in options:
                                temp.append(f"{seq} {opt}")
                        sequences = temp
                    else:
                        sequences = options
                elif word == "or":
                    options = [words[index-1], words[index+1]]
                    if len(sequences) > 0:
                        temp = []
                        for seq in sequences:
                            for opt in options:
                                temp.append(f"{' '.join(seq.split(' ')[:-1])} {opt}")
                        sequences = temp
                        skip = True
                    else:
                        sequences = options
                else:
                    temp = []
                    if len(sequences) > 0:
                        for seq in sequences:
                            temp.append(f"{seq} {word}")
                        sequences = temp
                    else:
                        sequences.append(word)

    else:
        sequences = line

    print(sequences)

Output:

['someone can tell', 'someone can figure']
['a squeaky wheel gets the grease', 'a squeaky wheel gets the oil', 'the squeaky wheel gets the grease', 'the squeaky wheel gets the oil']
['accounts for (someone', 'accounts for something)']
["that's (something for you", "that's someone) for you", "there's (something for you", "there's someone) for you"]

I wrote a script for you. But I can see, I was not the first one. I solved your problem by doing a recursive loop. I just pick a bracked and seperate these or a '/'. I can see, compared to the others it's more complex, but it can handle brackets as well (what only marcin archived before) and i think you can quickly adapt it.

Edit: I canged the main, so that one can run ist to open a textfile.
python3 bobafitsscript.py textfile

#!/usr/bin/env python3
def  sep_by_slash(word):
    pos=word.index('/')
    variation1=' '.join(wordlist[:i]+[]+wordlist[i+1:])
    variation2=' '.join(wordlist[:i]+[]+wordlist[i+1:])
    return (word[:pos],word[pos+1:])
    
def sep(s):
    if '(' in s:
        #veryfi if bracets are vorrect located.
        try:
            pos_start=s.index('(')
            pos_end=s.index(')')
            assert pos_start<pos_end
        except:
            print("ERROR: Mustake with brakets.")
            return None
        else:
            str_start=s[:pos_start]
            str_middle=s[pos_start+1:pos_end]
            str_end=s[pos_end+1:]
            if '/' in str_middle:
                variants=[
                    str_start+possible_word+str_end
                    for possible_word in str_middle.split('/')
                    ]
                return [finite for variant in variants for finite in sep(variant)]
            elif ' or ' in str_middle:
                variants=[
                    str_start+possible_word+str_end
                    for possible_word in str_middle.split(' or ')
                    ]
                return [finite for variant in variants for finite in sep(variant)]
            return [str_start+str_middle+str_end]
    if '/' in s:
        wordlist=s.split()
        for i,word in enumerate(wordlist):
            if '/' in word:
                variants=[
                    ' '.join(wordlist[:i]+[possible_word]+wordlist[i+1:])
                    for possible_word in word.split('/')
                    ]
                return [finite for variant in variants for finite in sep(variant)]
    return [s]

def main(args):
    with open(args[1], 'r') as textfile:
        for line in textfile.readlines():
            for var in sep(line.strip()):
                print(var)
    # ~ for line in sep(args[1]):
        # ~ print(line)
    return 0

def main_directly(args):
    for line in sep(args[1]):
        print(line)
    return 0

if __name__ == '__main__':
    import sys
    sys.exit(main(sys.argv))

Output:

someone can tell
someone can figure
a squeaky wheel gets the grease
a squeaky wheel gets the oil
the squeaky wheel gets the grease
the squeaky wheel gets the oil
accounts for someone
accounts for something
that's something for you
there's something for you
that's someone for you
there's someone for you
Related