21

I have the follwing string and I split it:

>>> st = '%2g%k%3p' >>> l = filter(None, st.split('%')) >>> print l ['2g', 'k', '3p'] 

Now I want to print the g letter two times, the k letter one time and the p letter three times:

ggkppp 

How is it possible?

5
  • Is this coming from html code? Commented Feb 8, 2016 at 9:42
  • 1
    Will the number before the letter ever be more than a single digit? Will the number ever be zero? Commented Feb 8, 2016 at 9:50
  • @Irano No this isnt .. Commented Feb 8, 2016 at 9:51
  • @PM Yes it may be mor than one digit... Commented Feb 8, 2016 at 9:52
  • 1
    You need a more formal/flushed out specification of the input language. What exactly does the % mean, what values for the number are valid, and what values for the "string to print" are valid? For example, what would %234 mean? Would it mean "print 34 twice," or is it invalid since no letter follows, or something else? What about %55a5? Print a5 fifty five times, maybe, or print 5a5 five times, or print 5 five times and then print a5? There are a ton of cases you haven't specified here. Commented Feb 8, 2016 at 23:51

5 Answers 5

15

You could use generator with isdigit() to check wheter your first symbol is digit or not and then return following string with appropriate count. Then you could use join to get your output:

''.join(i[1:]*int(i[0]) if i[0].isdigit() else i for i in l) 

Demonstration:

In [70]: [i[1:]*int(i[0]) if i[0].isdigit() else i for i in l ] Out[70]: ['gg', 'k', 'ppp'] In [71]: ''.join(i[1:]*int(i[0]) if i[0].isdigit() else i for i in l) Out[71]: 'ggkppp' 

EDIT

Using re module when first number is with several digits:

''.join(re.search('(\d+)(\w+)', i).group(2)*int(re.search('(\d+)(\w+)', i).group(1)) if re.search('(\d+)(\w+)', i) else i for i in l) 

Example:

In [144]: l = ['12g', '2kd', 'h', '3p'] In [145]: ''.join(re.search('(\d+)(\w+)', i).group(2)*int(re.search('(\d+)(\w+)', i).group(1)) if re.search('(\d+)(\w+)', i) else i for i in l) Out[145]: 'ggggggggggggkdkdhppp' 

EDIT2

For your input like:

st = '%2g_%3k%3p' 

You could replace _ with empty string and then add _ to the end if the work from list endswith the _ symbol:

st = '%2g_%3k%3p' l = list(filter(None, st.split('%'))) ''.join((re.search('(\d+)(\w+)', i).group(2)*int(re.search('(\d+)(\w+)', i).group(1))).replace("_", "") + '_' * i.endswith('_') if re.search('(\d+)(\w+)', i) else i for i in l) 

Output:

'gg_kkkppp' 

EDIT3

Solution without re module but with usual loops working for 2 digits. You could define functions:

def add_str(ind, st): if not st.endswith('_'): return st[ind:] * int(st[:ind]) else: return st[ind:-1] * int(st[:ind]) + '_' def collect(l): final_str = '' for i in l: if i[0].isdigit(): if i[1].isdigit(): final_str += add_str(2, i) else: final_str += add_str(1, i) else: final_str += i return final_str 

And then use them as:

l = ['12g_', '3k', '3p'] print(collect(l)) gggggggggggg_kkkppp 
Sign up to request clarification or add additional context in comments.

12 Comments

What happens when there are more than 9 of a letter?
''.join(i[1:]*int(i[0]) if i[0].isdigit() else i for i in ['12g', 'k', '3p']) = '2gkppp'
@Anton. imagin: st = '%2g_%3k%3p' so the result is:'g_g_kkkppp' but I want this result: 'gg_kkkppp'
@MLSC do you need that behavior only for _ sign?
@MLSC try edited version. You could extend it to what you like
|
13

One-liner Regex way:

>>> import re >>> st = '%2g%k%3p' >>> re.sub(r'%|(\d*)(\w+)', lambda m: int(m.group(1))*m.group(2) if m.group(1) else m.group(2), st) 'ggkppp' 

%|(\d*)(\w+) regex matches all % and captures zero or moredigit present before any word character into one group and the following word characters into another group. On replacement all the matched chars should be replaced with the value given in the replacement part. So this should loose % character.

or

>>> re.sub(r'%(\d*)(\w+)', lambda m: int(m.group(1))*m.group(2) if m.group(1) else m.group(2), st) 'ggkppp' 

3 Comments

can u explain why you used \b ?...because I think it's not necessary?
Test: re.sub(r'%(\d*)(\w*)', '-RE-',st) ---> '-RE--RE--RE-'
For lowercase letters only r'%(\d*)([a-z]+)'
11

Assumes you are always printing single letter, but preceding number may be longer than single digit in base 10.

seq = ['2g', 'k', '3p'] result = ''.join(int(s[:-1] or 1) * s[-1] for s in seq) assert result == "ggkppp" 

1 Comment

Or as a one-liner: result = ''.join([int(s[:-1] or 1) * s[-1] for s in st.split('%') if s]). You can give .join a generator expression but it's actually more efficient to give it a list comp. .join has to parse its input twice: the 1st pass determines the size of the output string, the 2nd pass builds the output. So if you give .join a gen exp it has to run the generator and build a list from it before it can start doing the actual joining.
7

LATE FOR THE SHOW BUT READY TO GO

Another way, is to define your function which converts nC into CCCC...C (ntimes), then pass it to a map to apply it on every element of the list l coming from the split over %, the finally join them all, as follows:

>>> def f(s): x = 0 if s: if len(s) == 1: out = s else: for i in s: if i.isdigit(): x = x*10 + int(i) out = x*s[-1] else: out = '' return out >>> st '%4g%10k%p' >>> ''.join(map(f, st.split('%'))) 'ggggkkkkkkkkkkp' >>> st = '%2g%k%3p' >>> ''.join(map(f, st.split('%'))) 'ggkppp' 

Or if you want to put all of these into one single function definition:

>>> def f(s): out = '' if s: l = filter(None, s.split('%')) for item in l: x = 0 if len(item) == 1: repl = item else: for c in item: if c.isdigit(): x = x*10 + int(c) repl = x*item[-1] out += repl return out >>> st '%2g%k%3p' >>> f(st) 'ggkppp' >>> >>> st = '%4g%10k%p' >>> >>> f(st) 'ggggkkkkkkkkkkp' >>> st = '%4g%101k%2p' >>> f(st) 'ggggkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkpp' >>> len(f(st)) 107 

EDIT :

In case of the presence of _ where the OP does not want this character to be repeated, then the best way in my opinion is to go with re.sub, it will make things easier, this way:

>>> def f(s): pat = re.compile(r'%(\d*)([a-zA-Z]+)') out = pat.sub(lambda m:int(m.group(1))*m.group(2) if m.group(1) else m.group(2), s) return out >>> st = '%4g_%12k%p__%m' >>> f(st) 'gggg_kkkkkkkkkkkkp__m' 

3 Comments

@MLSC...what's your expected output in this case? gg_kkkkkkkkkkkkkppp ?
Pardon I meant: But if I say st = %2g_%12k%3p result is not gg_kkkkkkkkkkkkkppp` or even when st = %2g___%12k%3p result must be gg___kkkkkkkkkkkkkppp
@MLSC .. no pblm .. :)
4

Loop the list, check first entry for number, and then append the second digit onwards:

string='' l = ['2g', 'k', '3p'] for entry in l: if len(entry) ==1: string += (entry) else: number = int(entry[0]) for i in range(number): string += (entry[1:]) 

1 Comment

string isn't a great variable name since it's the name of a standard Python module. Also, this answer won't work properly if a number has more than 1 digit.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.