Cycle a list from alternating sides

后端 未结 15 2105
温柔的废话
温柔的废话 2020-12-14 01:03

Given a list

a = [0,1,2,3,4,5,6,7,8,9]

how can I get

b = [0,9,1,8,2,7,3,6,4,5]

That is, produce a new lis

相关标签:
15条回答
  • 2020-12-14 01:34

    cycle between getting items from the forward iter and the reversed one. Just make sure you stop at len(a) with islice.

    from itertools import islice, cycle
    
    iters = cycle((iter(a), reversed(a)))
    b = [next(it) for it in islice(iters, len(a))]
    
    >>> b
    [0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
    

    This can easily be put into a single line but then it becomes much more difficult to read:

    [next(it) for it in islice(cycle((iter(a),reversed(a))),len(a))]
    

    Putting it in one line would also prevent you from using the other half of the iterators if you wanted to:

    >>> iters = cycle((iter(a), reversed(a)))
    >>> [next(it) for it in islice(iters, len(a))]
    [0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
    >>> [next(it) for it in islice(iters, len(a))]
    [5, 4, 6, 3, 7, 2, 8, 1, 9, 0]
    
    0 讨论(0)
  • 2020-12-14 01:37
    >>> [a[-i//2] if i % 2 else a[i//2] for i in range(len(a))]
    [0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
    

    Explanation:
    This code picks numbers from the beginning (a[i//2]) and from the end (a[-i//2]) of a, alternatingly (if i%2 else). A total of len(a) numbers are picked, so this produces no ill effects even if len(a) is odd.
    [-i//2 for i in range(len(a))] yields 0, -1, -1, -2, -2, -3, -3, -4, -4, -5,
    [ i//2 for i in range(len(a))] yields 0, 0, 1, 1, 2, 2, 3, 3, 4, 4,
    and i%2 alternates between False and True,
    so the indices we extract from a are: 0, -1, 1, -2, 2, -3, 3, -4, 4, -5.

    My assessment of pythonicness:
    The nice thing about this one-liner is that it's short and shows symmetry (+i//2 and -i//2).
    The bad thing, though, is that this symmetry is deceptive:
    One might think that -i//2 were the same as i//2 with the sign flipped. But in Python, integer division returns the floor of the result instead of truncating towards zero. So -1//2 == -1.
    Also, I find accessing list elements by index less pythonic than iteration.

    0 讨论(0)
  • 2020-12-14 01:41

    For fun, here is an itertools variant:

    >>> a = [0,1,2,3,4,5,6,7,8,9]
    >>> list(chain.from_iterable(izip(islice(a, len(a)//2), reversed(a))))
    [0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
    

    This works where len(a) is even. It would need a special code for odd-lengthened input.

    Enjoy!

    0 讨论(0)
  • 2020-12-14 01:41

    One way to do this for even-sized lists (inspired by this post):

    a = range(10)
    
    b = [val for pair in zip(a[:5], a[5:][::-1]) for val in pair]
    
    0 讨论(0)
  • 2020-12-14 01:43

    You can partition the list into two parts about the middle, reverse the second half and zip the two partitions, like so:

    a = [0,1,2,3,4,5,6,7,8,9]
    mid = len(a)//2
    l = []
    for x, y in zip(a[:mid], a[:mid-1:-1]):
        l.append(x)
        l.append(y)
    # if the length is odd
    if len(a) % 2 == 1:
        l.append(a[mid])
    print(l)
    

    Output:

    [0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
    
    0 讨论(0)
  • 2020-12-14 01:47

    Not at all elegant, but it is a clumsy one-liner:

    a = range(10)
    [val for pair in zip(a[:len(a)//2],a[-1:(len(a)//2-1):-1]) for val in pair]
    

    Note that it assumes you are doing this for a list of even length. If that breaks, then this breaks (it drops the middle term). Note that I got some of the idea from here.

    0 讨论(0)
提交回复
热议问题