Reputation: 329
I'm wondering if there is a way to have the default values of an "unmatched" item in a list set to the latest value in izip_longtest function.
The problem: i have a word which have a position on x, on y, and a rotation. Theses ones can be multiple and each letter of the word must have position/rotation.
More explicitely: i have two lists:
By example, if i have the word "HELLO" and x = [1,2,3,4,5]
, y = [6,7,8,9,10]
and r = [11,12,13,14,15]
.
The 'H' letter will have x = 1
, y = 6
, y = 11
The last letter 'O' will have x = 5
, y = 10
, r = 15
But, if the word is "PROBLEM" and x = [1]
, only the 'P' letter will match an x value and all the other letters will be set to None
with izip_longest
.
What i want is to "progagate" the latest or only one x
to all the other letters. So, please, If you have any suggestion or solution, tell me. Thanks by advance !
Upvotes: 1
Views: 479
Reputation: 11
As you've noticed, izip_longest
doesn't quite take you all the way there. You can wrap it, however, and take it across the finish line.
def izip_longest_last_value(*args, **kwargs):
last = (None,) * len(args)
for x in izip_longest(*args, **kwargs):
last = tuple(z if z is not None else last[y] for y,z in enumerate(x))
yield last
izip_longest
has a signature of *args, **kwargs, so we repeat that here. We also support an arbitrary number of positional arguments, so you can izip 1, 2, or 46 different lists together.
We initialize last to be a tuple as long as the number of positional args. Last will be updated to contain the values from the last iteration.
for x in izip_longest()
is us grabbing the current value from izip_longest, the meat of the wrapper.
last = tuple(...)
is us updating the last-tuple, and filling in either the current values from izip or the value from last if izip_longest
returned a None
for the space, indicating that it had been padded.
Upvotes: 1
Reputation: 80061
Since the default zip_longest
does not work for you here's a slightly smarter version:
import pprint
word = 'PROBLEM'
def zip_longest(*iterators):
last = {}
# Make a list of non-empty iterators
non_empty = dict.fromkeys(range(len(iterators)))
# Make sure it's an iterator, does no harm on iterators and helps
# on lists
iterators = map(iter, iterators)
while non_empty:
# Prepare the row
row = []
# Walk through the iterators in the given order
for i, iterator in enumerate(iterators):
# If there are still values, find the next
if i in non_empty:
try:
last[i] = iterator.next()
except StopIteration:
# No items anymore, this one is empty
del non_empty[i]
# Add the current (or last if it was already empty) value
row.append(last.get(i))
yield row
First/simple test:
x = [1]
y = [2]
pprint.pprint(list(zip_longest(word, x, y)))
[['P', 1, 2],
['R', 1, 2],
['O', 1, 2],
['B', 1, 2],
['L', 1, 2],
['E', 1, 2],
['M', 1, 2],
['M', 1, 2]]
Slightly more complicated:
x = range(3)
y = range(6)
pprint.pprint(list(zip_longest(word, x, y)))
[['P', 0, 0],
['R', 1, 1],
['O', 2, 2],
['B', 2, 3],
['L', 2, 4],
['E', 2, 5],
['M', 2, 5],
['M', 2, 5]]
With other longer ranges:
x = range(10)
y = range(5)
pprint.pprint(list(zip_longest(word, x, y)))
[['P', 0, 0],
['R', 1, 1],
['O', 2, 2],
['B', 3, 3],
['L', 4, 4],
['E', 5, 4],
['M', 6, 4],
['M', 7, 4],
['M', 8, 4],
['M', 9, 4],
['M', 9, 4]]
For completeness:
x = []
y = []
pprint.pprint(list(zip_longest(word, x, y)))
[['P', None, None],
['R', None, None],
['O', None, None],
['B', None, None],
['L', None, None],
['E', None, None],
['M', None, None],
['M', None, None]]
Upvotes: 0
Reputation: 1123420
Use itertools.cycle()
for the shorter input and do not use izip_longest()
:
from itertools import cycle, izip
izip(word, cycle(x), cycle(y), cycle(r))
Now x
, y
and r
will be cycled through to match up with the rest of the characters in word
:
>>> list(zip(word, cycle(x), cycle(y), cycle(r)))
[('P', 1, 6, 11), ('R', 2, 7, 12), ('O', 3, 8, 13), ('B', 4, 9, 14), ('L', 5, 10, 15), ('E', 1, 6, 11), ('M', 2, 7, 12)]
Upvotes: 0
Reputation: 52030
You will probably require your own custom zip iterator. Mostly based on izip_longest
but with some memory of the last not None
value.
There is probably room for improvement, but here is the spirit:
import itertools
def myzip(it1, it2):
last1 = None
last2 = None
for i in itertools.izip_longest(it1, it2):
if i[0] is not None:
last1 = i[0]
if i[1] is not None:
last2 = i[1]
yield((last1, last2))
And here is the "test case":
>>> lst1 = ('a', 'b')
>>> lst2 = (1,2,3,4,5)
>>> print list(myzip(lst1, lst2))
[('a', 1), ('b', 2), ('b', 3), ('b', 4), ('b', 5)]
>>> print list(myzip(lst2, lst1))
[(1, 'a'), (2, 'b'), (3, 'b'), (4, 'b'), (5, 'b')]
Upvotes: 0