254

What is an efficient way to repeat a string to a certain length? Eg: repeat('abc', 7) -> 'abcabca'

Here is my current code:

def repeat(string, length):
    cur, old = 1, string
    while len(string) < length:
        string += old[cur-1]
        cur = (cur+1)%len(old)
    return string

Is there a better (more pythonic) way to do this? Maybe using list comprehension?

John Howard
  • 61,037
  • 23
  • 50
  • 66

15 Answers15

746

Jason Scheirer's answer is correct but could use some more exposition.

First off, to repeat a string an integer number of times, you can use overloaded multiplication:

>>> 'abc' * 7
'abcabcabcabcabcabcabc'

So, to repeat a string until it's at least as long as the length you want, you calculate the appropriate number of repeats and put it on the right-hand side of that multiplication operator:

def repeat_to_at_least_length(s, wanted):
    return s * (wanted//len(s) + 1)

>>> repeat_to_at_least_length('abc', 7)
'abcabcabc'

Then, you can trim it to the exact length you want with an array slice:

def repeat_to_length(s, wanted):
    return (s * (wanted//len(s) + 1))[:wanted]

>>> repeat_to_length('abc', 7)
'abcabca'

Alternatively, as suggested in pillmod's answer that probably nobody scrolls down far enough to notice anymore, you can use divmod to compute the number of full repetitions needed, and the number of extra characters, all at once:

def pillmod_repeat_to_length(s, wanted):
    a, b = divmod(wanted, len(s))
    return s * a + s[:b]

Which is better? Let's benchmark it:

>>> import timeit
>>> timeit.repeat('scheirer_repeat_to_length("abcdefg", 129)', globals=globals())
[0.3964178159367293, 0.32557755894958973, 0.32851039397064596]
>>> timeit.repeat('pillmod_repeat_to_length("abcdefg", 129)', globals=globals())
[0.5276265419088304, 0.46511475392617285, 0.46291469305288047]

So, pillmod's version is something like 40% slower, which is too bad, since personally I think it's much more readable. There are several possible reasons for this, starting with its compiling to about 40% more bytecode instructions.

Note: these examples use the new-ish // operator for truncating integer division. This is often called a Python 3 feature, but according to PEP 238, it was introduced all the way back in Python 2.2. You only have to use it in Python 3 (or in modules that have from __future__ import division) but you can use it regardless.

Christopher Bottoms
  • 11,218
  • 8
  • 50
  • 99
zwol
  • 135,547
  • 38
  • 252
  • 361
  • 8
    No, OP wants the result to be of length 7 (which is not a multiple of 3). – IanS Mar 02 '17 at 14:24
  • 1
    I'm a bit conflicted because this isn't the correct answer for OP but is the correct answer for me and 489 other people... – Matt Fletcher Mar 16 '18 at 17:28
  • 2
    @MattFletcher You've just pushed me over the line from "I ought to rewrite this as an explainer of the accepted answer" to "I _will_ rewrite this..." ;-) – zwol Mar 16 '18 at 17:46
  • Pillmod's / Pillmuncher's code is more beautiful, but unfortunately slower than Scheirer's code, because adding to an already long string is an expensive operation (the Python runtime will have to copy the string to a new memory location), while truncating a temporary string is fast. In addition, assigning variables (and reserving their storage) is quite slow in Python. – Kai Petzke Aug 14 '20 at 17:45
82
def repeat_to_length(string_to_expand, length):
   return (string_to_expand * ((length/len(string_to_expand))+1))[:length]

For python3:

def repeat_to_length(string_to_expand, length):
    return (string_to_expand * (int(length/len(string_to_expand))+1))[:length]
Joop
  • 3,706
  • 34
  • 55
Jason Scheirer
  • 1,668
  • 16
  • 14
  • 6
    Looks like this is taking advantage of integer division. Doesn't that need to be `//` in Python 3? Or dropping the `+1` and using an explicit call to a ceiling function would suffice. Also, a note: the string generated actually has an extra repetition when it divides evenly; the extra gets cut off by the splice. That confused me at first. – jpmc26 May 03 '13 at 22:49
  • 1
    `int()` does the same thing here, but yea, `//` might be microscopically faster, because it does the divide & floor in one command instead of two. – Doyousketch2 Jan 15 '18 at 14:35
71

This is pretty pythonic:

newstring = 'abc'*5
print newstring[0:6]
Helen K
  • 719
  • 5
  • 3
37
def rep(s, m):
    a, b = divmod(m, len(s))
    return s * a + s[:b]
pillmuncher
  • 10,094
  • 2
  • 35
  • 33
15
from itertools import cycle, islice
def srepeat(string, n):
   return ''.join(islice(cycle(string), n))
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • This is what I use when I only need to iterate over the string (no join needed then). Let the python libraries do the job. – wihlke May 06 '18 at 19:01
7

Perhaps not the most efficient solution, but certainly short & simple:

def repstr(string, length):
    return (string * length)[0:length]

repstr("foobar", 14)

Gives "foobarfoobarfo". One thing about this version is that if length < len(string) then the output string will be truncated. For example:

repstr("foobar", 3)

Gives "foo".

Edit: actually to my surprise, this is faster than the currently accepted solution (the 'repeat_to_length' function), at least on short strings:

from timeit import Timer
t1 = Timer("repstr('foofoo', 30)", 'from __main__ import repstr')
t2 = Timer("repeat_to_length('foofoo', 30)", 'from __main__ import repeat_to_length')
t1.timeit()  # gives ~0.35 secs
t2.timeit()  # gives ~0.43 secs

Presumably if the string was long, or length was very high (that is, if the wastefulness of the string * length part was high) then it would perform poorly. And in fact we can modify the above to verify this:

from timeit import Timer
t1 = Timer("repstr('foofoo' * 10, 3000)", 'from __main__ import repstr')
t2 = Timer("repeat_to_length('foofoo' * 10, 3000)", 'from __main__ import repeat_to_length')
t1.timeit()  # gives ~18.85 secs
t2.timeit()  # gives ~1.13 secs
Adam Parkin
  • 17,891
  • 17
  • 66
  • 87
  • 1
    You could add a switch between the two versions based on the input and output lengths for maximum optimization. – Mad Physicist Jun 20 '16 at 21:37
  • @MadPhysicist That switch between the two versions would probably add about as much overhead, as the division in Jason Scheirer's answer costs. – Kai Petzke Aug 14 '20 at 17:30
6

i use this:

def extend_string(s, l):
    return (s*l)[:l]
김민준
  • 937
  • 11
  • 14
6

How about:

string * (length / len(string)) + string[0:(length % len(string))]
murgatroid99
  • 19,007
  • 10
  • 60
  • 95
  • `length / len(string)` needs to be wrapper in parenthesis, and you're missing the last `]`. – MikeWyatt Aug 02 '10 at 19:36
  • 1
    The most readable/intuitive so far, in my opinion. I think you need to use `//` for integer division in Python 3. The `0` in the splice is optional. (The colon is required, of course.) – jpmc26 May 03 '13 at 22:48
5

Not that there haven't been enough answers to this question, but there is a repeat function; just need to make a list of and then join the output:

from itertools import repeat

def rep(s,n):
  ''.join(list(repeat(s,n))
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
Amy Platt
  • 59
  • 1
  • 1
  • This does not answer the question. This one repeat the string X times, it does not repeat it until X length. E.g. `"abc", 4` would expect `"abca"`. This would create `abcabcabcabc` – Marcus Lind Apr 09 '19 at 07:01
3

Yay recursion!

def trunc(s,l):
    if l > 0:
        return s[:l] + trunc(s, l - len(s))
    return ''

Won't scale forever, but it's fine for smaller strings. And it's pretty.

I admit I just read the Little Schemer and I like recursion right now.

Kenan Banks
  • 207,056
  • 34
  • 155
  • 173
1

This is one way to do it using a list comprehension, though it's increasingly wasteful as the length of the rpt string increases.

def repeat(rpt, length):
    return ''.join([rpt for x in range(0, (len(rpt) % length))])[:length]
vezult
  • 5,185
  • 25
  • 41
1

Another FP aproach:

def repeat_string(string_to_repeat, repetitions):
    return ''.join([ string_to_repeat for n in range(repetitions)])
Aleš Kotnik
  • 2,654
  • 20
  • 17
0
def extended_string (word, length) :

    extra_long_word = word * (length//len(word) + 1)
    required_string = extra_long_word[:length]
    return required_string

print(extended_string("abc", 7))
Aditya Verma
  • 31
  • 1
  • 2
0
c = s.count('a')    
div=n//len(s)    
if n%len(s)==0:
    c= c*div
else:
    m = n%len(s)
    c = c*div+s[:m].count('a')
print(c)
0

Currently print(f"{'abc'*7}") generates:

abcabcabcabcabcabcabc
Jose Rondon
  • 370
  • 2
  • 6
  • 13