# Zip and more_itertools.unzip: Handling the zero length case

For a function that transposes a list using zip, there’s the case were the list of entries is empty, then zip returns an empty iterator instead of an iterator of empty containers. I understand that with no entries the zip function cannot deduce any dimensions, so it is reasonable to return nothing. What would be the best way of handling that? Is there a general way to solve this?

``````T = TypeVar("T")
U = TypeVar("U")
def transpose2(entries: Iterable[Tuple[T, U]]) -> Tuple[Iterable[T], Iterable[U]]:
# Broken when len(list(entries)) == 0
first, second = zip(*entries)
return first, second
``````

``````T = TypeVar("T")
U = TypeVar("U")
def transpose2(entries: Iterable[Tuple[T, U]]) -> Tuple[Iterable[T], Iterable[U]]:
# Ugly fix
first_second = tuple(zip(*entries))
first, second = (list(first_second), list(first_second)) if len(first_second) > 1 else ([], [])
return first, second
``````

This is toy function, here’s the function where I stumbled upon this

``````def scatter_annotated(
entries: Iterable[Tuple[str, Tuple[Number, Number]]],
ax: matplotlib.axes.Axes,
scatter_kwarg: Dict[str, Any],
annotate_kwarg: Dict[str, Any]):
"""Adds a scatter plot to the axis and annotates each point"""
def annotate(entry: Tuple[str, Tuple[Number, Number]]):
ax.annotate(*entry, **annotate_kwarg)
# more_itertools.side_effect
entries = side_effect(annotate, entries)
xy = unzip(((x, y) for _, (x, y) in entries))
# In the case of no entries, xy becomes an empty tuple instead
# of a tuple of two empty lists
x, y = (list(xy), list(xy)) if len(xy) > 1 else ([], [])
ax.scatter(x, y, scatter_kwarg)
``````

One possible solution I’m thinking is to have an unzip function that takes a dimention as an extra argument and returns an iterable of empty iterables with that size on the zero size case

This seems to solve my issue:

``````T = TypeVar("T")
def unzipd(iterable: Iterable[Sequence[T]], dimention: int
) -> Tuple[Iterator[T], ...]:
"""
Transposes iterable, similar to more_itertools.unzip
except it retuns the correct number of iterables even
when the iterable is empty
"""
if not dimention > 0:
raise ValueError(f"Dimention must be larger than 0, got {dimention}")
unzipped = unzip(iterable)
if len(unzipped) == dimention:
return unzipped
if len(unzipped) == 0:
return tuple(map(iter, [()]*dimention))
raise ValueError(f"Dimention provided does not match dimentions"
f"of unzipped iterable, provided {dimention}, "
f"got {len(unzipped)}")
``````

Do you mind making an issue at https://github.com/more-itertools/more-itertools/issues?

1 Like