Reputation: 411
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?
Upvotes: 0
Views: 600
Reputation: 5418
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
Upvotes: 0
Reputation: 156
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.
Upvotes: 0
Reputation: 1964
For reversing zip, this works,
for i, j in zip(*map(reversed, (list1, list2))):
...
Upvotes: 1
Reputation: 61498
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.
Upvotes: 0
Reputation: 1
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])
Upvotes: 0