On Sat, Nov 16, 2002 at 09:30:12PM +0900, Florian Frank wrote:
> > I notice that you've made this a method of Enumerable, rather than
> > Array.
> 
> I just needed the each iterator, so Array would perhaps be to
> restrictive. This way its possible to iterate over a tree of objects for
> example if it includes Enumerable.

You also needed the #[] method, that's why...

> > "abcde".zip
>     ==>[[97], [98], [99], [100], [101]]
> 
> The index of a string is a number. This only works because numbers
> happen to have a "size" method. This nice property doesn't hold for your
> example either:

...and....

> You can get strange results if you use "\n" in a string because "each"
> iterates over the lines of a string:
> 
> "abcdef\nABCDEFG".zip
>     ==>[[97, 65], [98, 66], [99, 67], [100, 68], [101, 69], [102, 70], [10, 71]]

Tried that with hashes already? :-)

For zip to work as an Enumerable's method, you have to rely on #each
only.  Just use Enumerable#to_a and you'll have the elements like
#each would have passed them to you.


module Enumerable
  def zip(*enumerables)
    zipped = []
    # was like this:
    # all = [self, *enumerables] 
    # now is:
    all = [self.to_a, *enumerables.collect {|e| e.to_a}]
    max_length = all.collect {|a| a.length}.max

    0.upto(max_length-1) do |i|
      zipped << all.collect {|a| a[i]}
    end
    zipped
  end
end

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

p "one\ntwo\n".zip("three\nfour\n")
#=> [["one\n", "three\n"], ["two\n", "four\n"]]

h = {:one => "two"}
p h.zip({:three => "four"})
#=> [[[:one, "two"], [:three, "four"]]]


You needn't obey String#each default blindly, either:

$/ = 'o'
p "one\ntwo\n".zip("three\nfour\n")
#=> [["o", "three\nfo"], ["ne\ntwo", "ur\n"], ["\n", nil]]


Massimiliano