0

I have a list like lst = ["Namaste", "Hello", "Ciao", "Salut"]. I want to iterate over overlapping pairs of values in reverse, printing each pair, to get:

Ciao, Salut
Hello, Ciao
Namaste, Hello

I know that I can use zip(lst, lst[1:]) to iterate forwards. However, if I try simply using reversed on the zip object, to iterate backwards:

lst = ["Namaste", "Hello", "Ciao", "Salut"]

for curr, nxt in reversed(zip(lst, lst[1:])):
    print(f'{curr}, {nxt}')

I get an error message that says TypeError: 'zip' object is not reversible.

How can I achieve my goal?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
tail
  • 355
  • 2
  • 11

6 Answers6

3

You can use zip:

l = ["Namaste", "Hello", "Ciao", "Salut"]

for a,b in zip(l[-2::-1], l[::-1]):
    print(f'{a}, {b}')

output:

Ciao, Salut
Hello, Ciao
Namaste, Hello
mozway
  • 194,879
  • 13
  • 39
  • 75
  • What if I want to print `Salut, Namaste` as well? – tail Sep 16 '22 at 15:52
  • Use: `for a,b in zip(l[-2::-1]+[l[-1]], l[::-1])` – mozway Sep 16 '22 at 15:55
  • This will create nearly three copies of the same list, seeing as though each slice makes a new copy. Depending on the size of the list, and how often this is used, that could be an issue. – aiootp Aug 12 '23 at 03:37
  • @aiootp if the lists are huge you can always use a generator. OP was originally using `zip` which is likely why I used the same logic ;) – mozway Aug 12 '23 at 05:42
1

For reversing zip, this works,

for i, j in zip(*map(reversed, (list1, list2))):
    ...
shaoyl85
  • 1,854
  • 18
  • 30
0

My code is kinda bulk but it works:

li = ["namaste", "hello", "ciao", "salut",'aymen','khalid']
if len(li)%2 != 0:
    li.append("")
for names in range(0,len(li),2):
    if names%2 == 0:
        print(li[names],li[names+1])
else:
    print(li[names])
0

Iterators created by zip cannot be reversed because zip can accept any sort of iterable - including iterators where the iterated elements will be determined on-demand, and might be unlimited. (Recall that in Python, an iterator is a type of iterable.)

Thus, reverse iteration over the inputs isn't necessarily possible at all; and if it is possible, zip has no way to know "where to start" (for example, given a list iterator, there is no easy way to access the original list, even though the iterator presumably holds a reference internally).

However, if the inputs are known to be reversible, we can simply reverse each before zipping:

>>> lst = ["Namaste", "Hello", "Ciao", "Salut"]
>>> for curr, nxt in zip(reversed(lst[:-1]), reversed(lst)):
...     print(f'{curr}, {nxt}')
... 
Ciao, Salut
Hello, Ciao
Namaste, Hello

Notice the slicing is different from the forwards-iterating version: for the curr iterator we must skip the last element of the list explicitly, since iteration will start at the end, but the next iterator can be made with the original list - we don't need to trim off the first element of the list, because it won't pair up. This is the opposite of how it works when iterating forwards.

Similarly, if we are starting with actual sequences, we can create reversed sequences and iterate over those. The simplest way is by slicing, and the simplest way to get "offset" iterators is to slice the offset one twice rather than doing the offset math:

>>> for curr, nxt in zip(lst[::-1][1:], lst[::-1]):
...     print(f'{curr}, {nxt}')
... 
Ciao, Salut
Hello, Ciao
Namaste, Hello

Again, because we iterate in reverse, it's the first input that needs to be offset.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
0

How can I achieve my goal?

Here's a solution that doesn't make unnecessary copies of lists:

>>> def reverse_pairwise_reader(sequence):
...     for i in range(-1, -len(sequence), -1):
...         yield sequence[i - 1], sequence[i]
...     yield sequence[-1], sequence[0]
...
>>> [*reverse_pairwise_reader(lst)]
... [('Ciao', 'Salut'),
     ('Hello', 'Ciao'),
     ('Namaste', 'Hello'),
     ('Salut', 'Namaste')]

This could save a substantial amount of memory, depending on how big your lists are.

aiootp
  • 154
  • 4
0

In Python 3.10 and up, this can be done concisely using itertools.pairwise on the reversed iterator of the list. We just need to remember to also reverse the order of each yielded pair, so that it returns to the "forward" direction:

from itertools import pairwise

lst = ["Namaste", "Hello", "Ciao", "Salut"]
for i, j in pairwise(reversed(lst)):
    print(f"{j}, {i}")

Output:

Ciao, Salut
Hello, Ciao
Namaste, Hello
slothrop
  • 3,218
  • 1
  • 18
  • 11