As the title suggests im trying to swap every other character in a string (ex: 'abcd' -> 'badc')
def coder(s):
ns = list(s)
ns[1::2],ns[::2] = ns[::2],ns[1::2]
return ''.join(ns)
My first attempt seen above worked fine with string that had an even number of characters, but gave the error ValueError: attempt to assign sequence of size x to extended slice of size x 1 with uneven numbers
def coder(s):
ns = list(s)
if len(s)%2==0:
ns[1::2],ns[::2] = ns[::2],ns[1::2]
else:
lastcar = ns[-1]
ns = ns[:-1]
ns = coder(ns) lastcar
return ''.join(ns)
my next attempt works fine but is more complex than I thought it would have to be
How to make this simpler?
CodePudding user response:
There are probably about a million different ways to do this in Python. One way would be to just iterate, as shown here:
def coder(s):
ns = list(s)
for i in range(0, (len(ns) - len(ns)%2) - 1, 2):
ns[i], ns[i 1] = ns[i 1], ns[i]
return ''.join(ns)
If you want to stick with extended slicing, just stop slicing at the last even digit location:
def coder(s):
ns = list(s)
end = len(ns)-len(ns)%2
ns[1::2], ns[:end:2] = ns[:end:2], ns[1::2]
return ''.join(ns)
CodePudding user response:
I would probably combine some known-good snippets that you should be familiar with (and if you're not -- get familiar with them they're crazy useful!)
From the itertools recipes list, chunk your string into groups of two, reverse each group, then chain them back together.
import itertools
# from itertools recipes
def grouper(iterable, n, *, incomplete='fill', fillvalue=None):
"Collect data into non-overlapping fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, fillvalue='x') --> ABC DEF Gxx
# grouper('ABCDEFG', 3, incomplete='strict') --> ABC DEF ValueError
# grouper('ABCDEFG', 3, incomplete='ignore') --> ABC DEF
args = [iter(iterable)] * n
if incomplete == 'fill':
return itertools.zip_longest(*args, fillvalue=fillvalue)
if incomplete == 'strict':
return zip(*args, strict=True)
if incomplete == 'ignore':
return zip(*args)
else:
raise ValueError('Expected fill, strict, or ignore')
s = "abcde"
def swap_chars(s):
"""
Swap every other letter of a string, returning the resulting mix.
>>> swap_chars('abcd')
'bacd'
>>> swap_chars('abcde')
'bacde'
"""
chunks = grouper(s, 2, fillvalue='')
return ''.join(itertools.chain.from_iterable(reversed(chunk) for chunk in chunks))
Alternatively, you could grab all the even-index characters in one list, all the odd-index characters in the other, and reassemble them with zip_longest
and chain.from_iterable
in reverse order.
import itertools
s = 'abcdefghijk'
def swap_chars(s):
mixed = itertools.zip_longest(s[1::2], s[0::2], fillvalue='')
return ''.join(itertools.chain.from_iterable(mixed))
This even generalizes fairly well to groups larger than 2
def swap_n_chars(s, n=2):
groups = [s[i::n] for i in range(n)]
mixed = itertools.zip_longest(*reversed(groups), fillvalue='')
return ''.join(itertools.chain.from_iterable(mixed))
However you might notice that you've just re-invented grouper
:)