4

If a have a string like abcd or 1234 etc. how can I print together, the first character, then the first two characters, then the first three etc. all together?

For example for a string = 1234 I would like to print/return 1121231234 or aababcabcd

I have this code so far:

def string_splosion(str):
    i = 0
    while i <= len(str):
        i += 1
        print(str[:i])
print(string_splosion('abcd'))

But it prints/returns it in separate lines. I could write it manually as print(str[0:1], str[1:2] <...>) but how do I make python do it as I don't know how long the string is going to be?

fejese
  • 4,601
  • 4
  • 29
  • 36
AL12
  • 51
  • 6

3 Answers3

6

You shouldn't use str as a variable name, because it shadows the built-in str type. You could join the sliced strings together in your loop:

def string_splosion(string):
    i, result = 0, ''
    while i < len(string): # < instead of <=
        i += 1
        result += string[:i]
    return result

It's possible to shorten your code a little using str.join and range:

def string_splosion(string):
    return ''.join(string[:i] for i in range(1, len(string) + 1))

or using itertools.accumulate (Python 3.2+):

import itertools
def string_splosion(string):
    return ''.join(itertools.accumulate(string))

itertools.accumulate approach appears to be 2 times faster than str.join one and about 1.5 times faster than the original loop-based solution:

string_splosion_loop(abcdef): 2.3944241080715223
string_splosion_join_gen(abcdef): 2.757582983268288
string_splosion_join_lc(abcdef): 2.2879220573578865
string_splosion_itertools(abcdef): 1.1873638161591886

The code I used to time the functions is

import itertools
from timeit import timeit

string = 'abcdef'

def string_splosion_loop():
    i, result = 0, ''
    while i < len(string):
        i += 1
        result += string[:i]
    return result

def string_splosion_join_gen():
    return ''.join(string[:i] for i in range(1, len(string) + 1))

def string_splosion_join_lc():
    # str.join performs faster when the argument is a list
    return ''.join([string[:i] for i in range(1, len(string) + 1)])

def string_splosion_itertools():
    return ''.join(itertools.accumulate(string))

funcs = (string_splosion_loop, string_splosion_join_gen, 
         string_splosion_join_lc, string_splosion_itertools)

for f in funcs:
    print('{.__name__}({}): {}'.format(f, string, timeit(f)))
vaultah
  • 44,105
  • 12
  • 114
  • 143
  • You are right about the string name, but that's what is used in the exercises. I usually change it to x or something when working on it and then change it back ( working at codingbat). your second line: i, result = o, '' I'm assuming the '' the same( i is 0, ''(the same) – AL12 Dec 28 '14 at 16:40
  • It is possible (maybe it's also worthless...) to strip a cycle in the list comprehension (proposed solution no.2) using `range(1,len(string)+1)`. – gboffi Dec 28 '14 at 17:14
  • @AL12 I'm not quite sure I understand, sorry. `i, result = 1, ''` assigns the empty string to the `result` variable and integer value 1 to `i` variable. This is called *sequence unpacking assignment*. – vaultah Dec 28 '14 at 17:43
4

Just use:

"".join([s[:i] for i in range(len(s)+1)])

As @abc noted, don't use str as a variable name because it's one of the default type. see https://docs.python.org/2/library/stdtypes.html#sequence-types-str-unicode-list-tuple-bytearray-buffer-xrange

E.g.:

>>> s = "1234"
>>> "".join([s[:i] for i in range(len(s)+1)])
'1121231234'
>>> s = "abcd"
>>> "".join([s[:i] for i in range(len(s)+1)])
'aababcabcd'

range(len(s)+1) is because of slicing, see Explain Python's slice notation:

>>> s = "1234"
>>> len(s)
4
>>> range(len(s))
[0, 1, 2, 3]
>>> s[:3]
'123'
>>> range(len(s)+1)
[0, 1, 2, 3, 4]
>>> s[:4]
'1234'

Then:

>>> s[:0]
''
>>> s[:1]
'1'
>>> s[:2]
'12'
>>> s[:3]
'123'
>>> s[:4]
'1234'

Lastly, join list([s[:1], s[:2], s[:3], s[:4]]) using "".join(list), see https://docs.python.org/2/library/string.html#string.join:

>>> list([s[:1], s[:2], s[:3], s[:4]])
['1', '12', '123', '1234']
>>> x = list([s[:1], s[:2], s[:3], s[:4]])
>>> "".join(x)
'1121231234'
>>> "-".join(x)
'1-12-123-1234'
>>> " ".join(x)
'1 12 123 1234'

To avoid extract iteration in loop, you can use range(1,len(s)+1) since s[:0] returns string of 0 length:

>>> s = "1234"
>>> "".join([s[:i] for i in range(1,len(s)+1)])
'1121231234'
>>> "".join([s[:i] for i in range(len(s)+1)])
'1121231234'
Community
  • 1
  • 1
alvas
  • 115,346
  • 109
  • 446
  • 738
  • in your code: "".join([s[:i] for i in range(len(s)+1)]) what does the "" at the beginning do? – AL12 Dec 28 '14 at 17:23
  • updated the answer, hope the examples on `str.join()` helps, IMHO, you should also go and take a look at the documentations when the answer goes "see ..." – alvas Dec 28 '14 at 20:01
0

If you are using python 3 you can use this to print without a newline:

print(yourString, end="")

So your function could be:

def string_splosion(str):
    for i in range(len(str)):
        print(str[:i], end="")
print(string_splosion('abcd'))
danijar
  • 32,406
  • 45
  • 166
  • 297
Abdelrahman Elkady
  • 2,518
  • 2
  • 21
  • 30