efficient way to split multi-word hashtag in python

Viewed 679

Given a text like

THIS is a #hashtag and this is a #multiWordHashtag

I need to output

THIS is a hashtag and this is a multi Word Hashtag

For now, I use this function:

def do_process_eng_hashtag(input_text: str):
    result = []
    for word in input_text.split():
        if word.startswith('#') and len(word) > 1:
            word = list(word)
            word[1] = word[1].upper()
            word = ''.join(word)
            word = ' '.join(re.findall('[A-Z][^A-Z]*', word))
        result.append(word)
    return ' '.join(result)

But I wonder if there is a more efficient and neat way to do so?

2 Answers

Using re.sub:

You can specify replacement function:

def do_process_eng_hashtag(input_text: str) -> str:
    return re.sub(
        r'#[a-z]\S*',
        lambda m: ' '.join(re.findall('[A-Z][^A-Z]*|[a-z][^A-Z]*', m.group().lstrip('#'))),
        input_text,
    )

The replacement function (lambda) will split hash tag into multiple words:

>>> re.findall('[A-Z][^A-Z]*|[a-z][^A-Z]*', '#multiWordHashtag'.lstrip('#'))
['multi', 'Word', 'Hashtag']
>>> do_process_eng_hashtag('THIS is a #hashtag and this is a #multiWordHashtag')
'THIS is a hashtag  and this is a multi Word Hashtag '

You can use a function with re.sub like so:

import re 

example='THIS is a #hashtag and this is a #multiWordHashtag'

def rep(m):
    s=m.group(1)
    return ' '.join(re.split(r'(?=[A-Z])', s))

>>> re.sub(r'#(\w+)', rep, example)
THIS is a hashtag and this is a multi Word Hashtag

Works like this:

  1. re.sub(r'#(\w+)', rep, example) calls rep function with a match group for all hashtags.

  2. The rep function then uses a lookahead to split the string on capitalization:

>>> re.split(r'(?=[A-Z])','multiWordHashtag')
['multi', 'Word', 'Hashtag']
>>> re.split(r'(?=[A-Z])','hastag')
['hastag']

The ' '.join() adds space delimiters. If there are no capitals, (ie, the argument to join is a list of length 1), just the string is returned.

You can modify the regex in re.sub(r'#(\w+)', rep, example) to whatever YOU consider a hashtag. Perhaps re.sub(r'#([a-zA-Z]+)', rep, example)?


Alternatively, you can combine Python splitting with the same regex to detect upper case:

def word_func(s):
    return ' '.join(re.split(r'(?=[A-Z])', s[1:]))

' '.join([word_func(s) if s.startswith('#') else s for s in example.split()])
# same output
Related