Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

enumerate and zip within Coffeescript?

Coming from Python, I like many of the features that Coffeescript borrows from Python and Perl (ranges/slices, comprehensions, destructuring assignments). Is there any syntactic sugar in Coffeescript to mimic Python's enumerate or zip (itertools.izip) functions?

Here are the patterns that I don't care much for:

# an enumerate call would be helpful here
i = 0
for x in arr
  ... use x and i ...
  i++

and

# a zip would be useful here
n = Math.min(arr1.length,arr2.length)
for i in 0...n
  x = arr1[i]; y = arr2[i]
  ... use x and y ...
like image 406
yonran Avatar asked Apr 11 '11 01:04

yonran


3 Answers

forEach is effectively built in:

a = ['a','b','c']
for el,i in a
    alert "Element #{el} is at index #{i}"
like image 148
Mark Smith Avatar answered Nov 12 '22 21:11

Mark Smith


Enumerate:

arr.forEach (x, i) ->
    ... use x and i ...

zip / zipWith (I learned these from Haskell; I assume they mean the same thing in Python?):

zip = (arr1, arr2) ->
  basic_zip = (el1, el2) -> [el1, el2]
  zipWith basic_zip, arr1, arr2

zipWith = (func, arr1, arr2) ->
  min = Math.min arr1.length, arr2.length
  ret = []

  for i in [0...min]
    ret.push func(arr1[i], arr2[i])

  ret

Some examples (tested):

zip([1, 2, 3], [4, 5, 6])          # => [[1, 4], [2, 5], [3, 6]]

add = (el1, el2) -> el1 + el2
zipWith(add, [1, 2, 3], [4, 5, 6]) # => [5, 7, 9]

Update: Reimplemented Haskell-style, just for fun. Not as cool without the pattern matching, but oh well..

zipWith = (func, arr1, arr2) ->
  return [] if arr1.length is 0 or arr2.length is 0

  el1 = arr1.shift()
  el2 = arr2.shift()

  ret_arr = zipWith func, arr1, arr2
  ret_arr.unshift func(el1, el2)

  ret_arr

Oh, man, this was fun. SO needs more questions like this :D

Gist for zip and zipWith

like image 13
Jon Gauthier Avatar answered Nov 12 '22 22:11

Jon Gauthier


For zipping and other such utility functions, Underscore.js is pretty much the standard library—and it happens to have been created by Jeremy Ashkenas, the man behind CoffeeScript. With it, you could write your zip example as

for elems in _.zip(arr1, arr2)
  x = elems[0]; y = elems[1]
  ...

or better yet

for [x, y] in _.zip(arr1, arr2)
  ...

using pattern-matching. Note, however, that _.zip uses the max length of arr1 and arr2, not the min; so if you don't want to handle undefined values, you should truncate the longer array first.

There's also a CoffeeScript implementation of Underscore, Underscore.coffee, which is a great place to look if you're wondering how to implement a particular loop in CoffeeScript.

like image 8
Trevor Burnham Avatar answered Nov 12 '22 22:11

Trevor Burnham