Someone asked How to do Python’s zip in C#?...
...which leads me to ask, what good is zip? In what scenarios do I need this? Is it really so foundational that I need this in the base class library?
Someone asked How to do Python’s zip in C#?...
...which leads me to ask, what good is zip? In what scenarios do I need this? Is it really so foundational that I need this in the base class library?
It allows you to process sequences in parallel instead of sequentially or nested. There's... so many uses for it that they currently escape me.
Here's a common use case for zip:
x = [1,2,3,4,5]
y = [6,7,8,9,0]
for x,y in zip(x,y):
print x+y
A use case:
>>> fields = ["id", "name", "location"]
>>> values = ["13", "bill", "redmond"]
>>> dict(zip(fields, values))
{'location': 'redmond', 'id': '13', 'name': 'bill'}
Try doing this without zip...
zip is useful if you'd like to iterate over multiple iterables simultaneously, which is a reasonably common scenario in Python.
One real-world scenario where zip has come in handy for me is if you have an M by N array, and you want to look at columns instead of rows. For example:
>>> five_by_two = ((0, 1), (1, 2), (2, 3), (3, 4), (4, 5))
>>> two_by_five = tuple(zip(*five_by_two))
>>> two_by_five
((0, 1, 2, 3, 4), (1, 2, 3, 4, 5))
Here's a case where I used zip()
to useful effect, in a Python class for comparing version numbers:
class Version(object):
# ... snip ...
def get_tuple(self):
return (self.major, self.minor, self.revision)
def compare(self, other):
def comp(a, b):
if a == '*' or b == '*':
return 0
elif a == b:
return 0
elif a < b:
return -1
else:
return 1
return tuple(comp(a, b) for a, b in zip(self.get_tuple(), Version(other).get_tuple()))
def is_compatible(self, other):
tup = self.compare(other)
return (tup[0] == 0 and tup[1] == 0)
def __eq__(self, other):
return all(x == 0 for x in self.compare(other))
def __ne__(self, other):
return any(x != 0 for x in self.compare(other))
def __lt__(self, other):
for x in self.compare(other):
if x < 0:
return True
elif x > 0:
return False
return False
def __gt__(self, other):
for x in self.compare(other):
if x > 0:
return True
elif x < 0:
return False
return False
I think zip()
, coupled with all()
and any()
, makes the comparison operator implementations particularly clear and elegant. Sure, it could have been done without zip()
, but then the same could be said about practically any language feature.
It's handy in different places. My favorite, from http://norvig.com/python-iaq.html, is transposing a matrix:
>>> x = [ [1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15]]
>>> zip(*x)
[(1, 6, 11), (2, 7, 12), (3, 8, 13), (4, 9, 14), (5, 10, 15)]
Someone actually asked a question here fairly recently that I answered with the Zip
extension method, so it's obviously important for some people. ;)
Actually, it is a fairly important operation for mathematical algorithms - matrices, curve fitting, interpolation, pattern recognition, that sort of thing. Also very important in engineering applications like digital signal processing where much of what you do is combine multiple signals or apply linear transforms to them - both are based on the sample index, hence, zip it. Zipping two sequences is far, far faster than sorting and joining them based on some key, especially when you know in advance that the sequences have the same number of elements and are in the same order.
I can't get into tight specifics here on account of my current employment, but speaking generally, this is also valuable for telemetry data - industrial, scientific, that sort of thing. Often you'll have time sequences of data coming from hundreds or thousands of points - parallel sources - and you need to aggregate, but horizontally, over devices, not over time. At the end, you want another time sequence, but with the sum or average or some other aggregate of all the individual points.
It may sound like a simple sort/group/join in SQL Server (for example) but it's actually really hard to do efficiently this way. For one thing, the timestamps may not match exactly, but you don't care about differences of a few milliseconds, so you end up having to generate a surrogate key/row number and group on that - and of course, the surrogate row number is nothing more than the time index which you already had. Zipping is simple, fast, and infinitely parallelizable.
I don't know if I'd call it foundational, but it it is important. I don't use the Reverse
method very often either, but by the same token I'm glad I don't have to keep writing it myself on those rare occasions when I do find a need for it.
One of the reasons it might not seem that useful to you now is that .NET/C# 3.5 does not have tuples. C# 4 does have tuples, and when you're working with tuples, zipping really is a fundamental operation because ordering is strictly enforced.