Home > OS >  Swap every other char in uneven string
Swap every other char in uneven string

Time:03-03

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 :)

  • Related