views:

272

answers:

6

I am a newbie and seeking for the Zen of Python :) Today's koan was finding the most Pythonesq way to solve the following problem:

Permute the letters of a string pairwise, e.g.

input:  'abcdefgh'
output: 'badcfehg'
+13  A: 

I'd go for:

s="abcdefgh"
print "".join(b+a for a,b in zip(s[::2],s[1::2]))

s[start:end:step] takes every step'th letter, zip matches them up pairwise, the loop swaps them, and the join gives you back a string.

Anthony Towns
Damn, I was just typing this myself!
Will
+1  A: 

Since in Python, every string is also an iterable, itertools comes in handy here.

In addition to the functions itertools provides, the documentation also supplies lots of recipes.

from itertools import izip_longest

# From Python 2.6 docs
def grouper(n, iterable, fillvalue=None):
 "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
 args = [iter(iterable)] * n
 return izip_longest(fillvalue=fillvalue, *args)

Now you can use grouper to group the string by pairs, then reverse the pairs, then join them back into a string.

pairs = grouper(2, "abcdefgh")
reversed_pairs = [''.join(reversed(item)) for item in pairs]
print ''.join(reversed_pairs)
Jason R. Coombs
+5  A: 
''.join(s[i+1] + s[i] for i in range(0,len(s),2))

Yes, I know it's less pythonic for using range, but it's short, and I probably don't have to explain it for you to figure out what it does.

Laurence Gonsalves
If you change it to ''.join(s[i]+s[i-1] for i in range(1,len(s),2)) you avoid the exception on odd-length input, fwiw.
Anthony Towns
why is range() less pythonic?
matt b
matt: using indices to get elements from a sequence is often viewed as non-pythonic.
Laurence Gonsalves
Anthony: true, but then it just silently does the wrong thing (which the accepted answer does to, incidentally). I'd rather have it raise an exception than silently lose data.
Laurence Gonsalves
+6  A: 

my personal favorite to do stuff pairwise:

def pairwise( iterable ):
   it = iter(iterable)
   return zip(it, it) # zipping the same iterator twice produces pairs

output = ''.join( b+a for a,b in pairwise(input))
THC4k
really COOL! :-D
fortran
+1 for using iterators for what they do well -- keeping track of the sequence "cursor"
kaizer.se
`n_tuples = (lambda iterable, n: zip(*[iter(iterable)]*n))`
kaizer.se
that's very clever. It is not obvious what it is doing, but darned if it doesn't work.
tottinge
+2  A: 

I just noticed that none of the existing answers work if the length of the input is odd. Most of the answers lose the last character. My previous answer throws an exception.

If you just want the last character tacked onto the end, you could do something like this:

print "".join(map(lambda a,b:(b or '')+a, s[::2], s[1::2]))

or in 2.6 and later:

print "".join(b+a for a,b in izip_longest(s[::2],s[1::2], fillvalue=''))

This is based on Anthony Towns's answer, but uses either map or izip_longest to make sure the last character in an odd-length string doesn't get discarded. The (b or '') bit in the map version is to convert the None that map pads with into ''.

Laurence Gonsalves
A: 

This may look a little scary, but I think you'd learn a lot deciphering the following idiom:

s = "abcdefgh"
print ''.join(b+a for a,b in zip(*[iter(s)]*2))
Triptych