4

The function I have to build is meant to replace digits in a string by (value of digit * next character).

So, foo = '2 hs4q q2w2 ' will become ' hsqqqq qww ' (mind the spaces)

Assumption - Digit can't be zero.

I fetched the (index,value) of digits and next char. Used that info to get the substrings I need to put back into the string:

foo = '2 hs4q q2w2 '

parameters=[(int(foo_list[b]),b+1) for b in range(len(foo_list)) if foo_list[b].isdigit()]
parameters # list of tuples (digit,charindex to be extended)
#[(2, 1), (4, 5), (2, 9), (2, 11)]

for p,i in parameters:
    hoo=p*foo[i]
    print (hoo,type(hoo))

 #Out
   <class 'str'> # Two spaces
qqqq <class 'str'>
ww <class 'str'>
   <class 'str'> # Two spaces

How can I use all this info in a loop that works with similar strings? I understand strings are immutable, hence a new str object has to be created for every insert/replace. Plus the index values change as the loop runs.

Comments after solution -

Thank you all for four different kinds of solutions, here is a reference for anyone who hasn't used yield from, yield - In practice, what are the main uses for the new “yield from” syntax in Python 3.3?

pyeR_biz
  • 986
  • 12
  • 36

5 Answers5

3

You can use re.sub:

import re
foo = '2 hs4q q2w2 '
new_foo = re.sub('\d+[\w\W]{1}', lambda x:x.group()[-1]*int(x.group()[:-1]), foo)

Output:

'  hsqqqq qww  '
Ajax1234
  • 69,937
  • 8
  • 61
  • 102
3

Single digit numbers

You can check if a character is a digit with str.isdigit, if it is then cast it to an int and multiply it with the next character. This logic can be written as a generator given to str.join.

Code

def expand_string(s):
    return ''.join([(int(c) - 1) * s[i+1] if c.isdigit() else c for i, c in enumerate(s)])

Example

foo = '2 hs4q q2w2 '
print(expand_string(foo)) # '  hsqqqq qww  '

Although, the above fails for a string with multiple digit number such as f10o'.

Multiple digits numbers

If you also want to consider numbers with multiple digits, you can write a generator function that groups digits together using itertools.groupby.

Code

from itertools import groupby

def group_digits(s):
    for isdigit, group in groupby(s, str.isdigit):
        yield from [''.join(group)] if isdigit else group

def expand_string(s):
    s = list(group_digits(s))
    return ''.join((int(c) - 1) * s[i+1] if c.isdigit() else c for i, c in enumerate(s))

Example

foo = 'f10o'
print(expand_string(foo)) # 'foooooooooo'
Community
  • 1
  • 1
Olivier Melançon
  • 21,584
  • 4
  • 41
  • 73
1

One idea is to iterate characters in your string pairwise using zip (or itertools.zip_longest) and yield an item depending on whether the character is a digit.

from itertools import zip_longest, islice

foo = '2 hs4q q2w2 '

def gen(x):
    for i, j in zip_longest(x, islice(x, 1, None), fillvalue=''):
        if i.isdigit():
            yield j * (int(i)-1)
        else:
            yield i

res = ''.join(list(gen(foo)))

# '  hsqqqq qww  '
jpp
  • 159,742
  • 34
  • 281
  • 339
1

Be careful while using next index in the current iteration, It can give you error because if the string is something like foo = '2 hs4q q2w2 2' Use try and except

foo = '2 hs4q q2w2 '

splitted_data=list(foo)

for i,j in enumerate(splitted_data):
    try:
        if j.isdigit():
            splitted_data[i]=splitted_data[i+1]*int(j)
    except IndexError:
        pass

print("".join(splitted_data))

output:

   hsqqqqq qwww   
Aaditya Ura
  • 12,007
  • 7
  • 50
  • 88
0

Such a function can be obtained using a while loop:

def new_string(string):
    # Create the variable new_string
    new_string = ""
    i = 0
    while i < len(string):
        char = string[i]
        # If char is a digit we multiply the next char by it
        if char.isdigit():
            new_string += int(char)*string[i+1]
            i += 2
        # If not we just concatenate char
        else:
            new_string += char
            i += 1
    return new_string

Hope it helps.