Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Switch every pair of characters in a string

For example, having the string:

abcdefghijklmnopqrstuvwxyz

should result in something like this:

badcfehgjilknmporqtsvuxwzy

How do I even go about it?

I thought of something not very efficient, such as:

s = str(range(ord('a'), ord('z') + 1))
new_s = ''
for i in xrange(len(s)):
    if i != 0 and i % 2 == 0:
        new_s += '_' + s[i]
    else:
        new_s += s[i]
# Now it should result in a string such as 'ab_cd_ef_...wx_yz'
l = new_s.split('_')
for i in xrange(len(l)):
    l[i] = l[i][::-1]
result = str(l)

Is there any better way ? Some way that is more efficient or more general so I could also go about it with 3 letters more easily ?

like image 972
Jack Avatar asked Jun 03 '15 18:06

Jack


2 Answers

You can use zip() function which woud return a list of tuples as [(b,a), (d,c), ...] and the applying .join() method to both the elements of the tuple and list as well.

a = "abcdefghijklmnopqrstuvwxyz"
# a[::2] = "acegikmoqsuwy"
# a[1::2] = "bdfhjlnprtvx"
print "".join("".join(i) for i in zip(a[1::2], a[::2]))
>>> badcfehgjilknmporqtsvuxwzy

EDIT: To handle the case of odd length strings, as suggested by @Ashwini and @TigerhawkT3, you may change the code as:

print "".join("".join(i) for i in zip(a2, a1)) + a[-1] if len(a)%2 else '' 
like image 80
ZdaR Avatar answered Sep 28 '22 00:09

ZdaR


One solution without using any imports will be to convert string to an iterator and during the iteration fetch the next character by calling next on the iterator:

>>> s = "abcdefghijklmnopqrstuvwxyz"
>>> it = iter(s)
>>> ''.join(next(it, '') + c for c in it )
'badcfehgjilknmporqtsvuxwzy'

Timings:

>>> s = "abcdefghijklmnopqrstuvwxyz" * 10**5
>>> def func_next_no_cache(s):
    it = iter(s)
    return ''.join([next(it, '') + c for c in it])
...
>>> %timeit func_next_no_cache(s)
1 loops, best of 3: 291 ms per loop

But the calls to next are actually slowing it down because for finding next Python has to go to the builtins starting from local scope, let's cache it and try again:

>>> def func_next_cache(s, next=next):
    it = iter(s)
    return ''.join([next(it, '') + c for c in it])
...
>>> %timeit func_next_cache(s)
1 loops, best of 3: 241 ms per loop

But the fastest solution will be to use itertools.izip_longest:

>>> from itertools import izip_longest
>>> def func_izip_l(s):
    it = iter(s)
    return "".join([b+a for a, b in  izip_longest(it, it, fillvalue='')])
...
>>> %timeit func_izip_l(s)

1 loops, best of 3: 209 ms per loop

@Joran's code is also very close to this one when used with a list instead of generator expression, but it creates two additional strings in memory:

>>> %timeit "".join([b+a for a, b in izip_longest(s[::2], s[1::2], fillvalue="")])
1 loops, best of 3: 212 ms per loop

Note that we should always feed a list to str.join if speed is a concern: https://stackoverflow.com/a/9061024/846892

like image 41
Ashwini Chaudhary Avatar answered Sep 28 '22 01:09

Ashwini Chaudhary