What is the best way to iterate through two containers of the same length?

M

Mark Watson

If I have two containers c1 and c2 of the same length, what is the
proper "Ruby way" to do this:

c1.length.times {|i|
# access c1, c2
}

I like that Ruby container classes provide their own iterators, but
what I would like to have is something like:

(c1,c2).each {|x1,x2| .... }

I thought of writing my own iterator class so that I could do something
like:

Iterator.new(c1,c2).each {|x1,x2| .... }

but that looks clumsy and inefficient.

I am transitioning to using mostly Ruby (moving away from Java, Lisp,
and Smalltalk) and I would like to use the proper Ruby idioms.
 
R

Robert Klemme

2006/3/6 said:
If I have two containers c1 and c2 of the same length, what is the
proper "Ruby way" to do this:

c1.length.times {|i|
# access c1, c2
}

I like that Ruby container classes provide their own iterators, but
what I would like to have is something like:

(c1,c2).each {|x1,x2| .... }

I thought of writing my own iterator class so that I could do something
like:

Iterator.new(c1,c2).each {|x1,x2| .... }

but that looks clumsy and inefficient.

I am transitioning to using mostly Ruby (moving away from Java, Lisp,
and Smalltalk) and I would like to use the proper Ruby idioms.


If the two are arrays you can use Array#zip:
%w{foo bar baz}.zip([1,2,3]) {|a,b| print a, "-", b,"\n"}
foo-1
bar-2
baz-3

For the more general case you can look at Generator
http://ruby-doc.org/stdlib/libdoc/generator/rdoc/

Kind regards

robert
 
J

James Edward Gray II

If I have two containers c1 and c2 of the same length, what is the
proper "Ruby way" to do this:

c1.length.times {|i|
# access c1, c2
}

I like that Ruby container classes provide their own iterators, but
what I would like to have is something like:

(c1,c2).each {|x1,x2| .... }


You are looking for Enumerable#zip:
letters = %w{A B C} => ["A", "B", "C"]
numbers = [1, 2, 3] => [1, 2, 3]
letters.zip(numbers) do |letter, number| ?> puts "#{letter}#{number}"
end
A1
B2
C3
=> nil

You can also use the standard generator library to turn Ruby's
internal iterators into external iterators (like Java's iterators) if
needed.

Hope that helps.

James Edward Gray II
 
G

gordon

How about:


foo = ["foo","foo"]
bar = ["bar","bar"]

foo.zip(bar).each do |a,b|
puts "#{a} #{b}"
end
 
L

Logan Capaldo

If I have two containers c1 and c2 of the same length, what is the
proper "Ruby way" to do this:

c1.length.times {|i|
# access c1, c2
}

I like that Ruby container classes provide their own iterators, but
what I would like to have is something like:

(c1,c2).each {|x1,x2| .... }

I thought of writing my own iterator class so that I could do
something
like:

Iterator.new(c1,c2).each {|x1,x2| .... }

but that looks clumsy and inefficient.

I am transitioning to using mostly Ruby (moving away from Java, Lisp,
and Smalltalk) and I would like to use the proper Ruby idioms.


Well there is zip:
irb(main):001:0> [1,2,3].zip([4,5,6]) do |a, b|
irb(main):002:1* puts "#{a} #{b}"
irb(main):003:1> end
1 4
2 5
3 6
=> nil
 
W

Wilson Bilkovich

If I have two containers c1 and c2 of the same length, what is the
proper "Ruby way" to do this:

c1.length.times {|i|
# access c1, c2
}

I like that Ruby container classes provide their own iterators, but
what I would like to have is something like:

(c1,c2).each {|x1,x2| .... }

I thought of writing my own iterator class so that I could do something
like:

Iterator.new(c1,c2).each {|x1,x2| .... }

but that looks clumsy and inefficient.

I am transitioning to using mostly Ruby (moving away from Java, Lisp,
and Smalltalk) and I would like to use the proper Ruby idioms.


One way I'm fond of is:
require 'generator'
enum =3D SyncEnumerator.new([1,2,3], [7,8,9])
enum.each do |pair|
puts pair.inspect
end
# Results in:
[1, 7]
[2, 8]
[3, 9]
 
W

William James

Mark said:
If I have two containers c1 and c2 of the same length, what is the
proper "Ruby way" to do this:

c1.length.times {|i|
# access c1, c2
}

I like that Ruby container classes provide their own iterators, but
what I would like to have is something like:

(c1,c2).each {|x1,x2| .... }

I thought of writing my own iterator class so that I could do something
like:

Iterator.new(c1,c2).each {|x1,x2| .... }

but that looks clumsy and inefficient.

I am transitioning to using mostly Ruby (moving away from Java, Lisp,
and Smalltalk) and I would like to use the proper Ruby idioms.


foo = %w(x y z) ; bar = [2,4,6]
[foo, bar].transpose.each{|a,b| print a, b, $/ }
--->
x2
y4
z6
 
D

dblack

Hi --

Mark said:
If I have two containers c1 and c2 of the same length, what is the
proper "Ruby way" to do this:

c1.length.times {|i|
# access c1, c2
}

I like that Ruby container classes provide their own iterators, but
what I would like to have is something like:

(c1,c2).each {|x1,x2| .... }

I thought of writing my own iterator class so that I could do something
like:

Iterator.new(c1,c2).each {|x1,x2| .... }

but that looks clumsy and inefficient.

I am transitioning to using mostly Ruby (moving away from Java, Lisp,
and Smalltalk) and I would like to use the proper Ruby idioms.


foo = %w(x y z) ; bar = [2,4,6]
[foo, bar].transpose.each{|a,b| print a, b, $/ }
--->
x2
y4
z6


I don't think $/ is very idiomatic. See the ToDo file in the source;
it includes:

* discourage use of symbol variables (e.g. $/, etc.) in manual

:)


David

--
David A. Black ([email protected])
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

"Ruby for Rails" chapters now available
from Manning Early Access Program! http://www.manning.com/books/black
 
M

Mark Watson

Thanks everyone - just wht I was looking for. The SyncEnumerator class
is fine in general, and using zip is what I wanted for arrays.
 
W

William James

Mark said:
If I have two containers c1 and c2 of the same length, what is the
proper "Ruby way" to do this:

c1.length.times {|i|
# access c1, c2
}

I like that Ruby container classes provide their own iterators, but
what I would like to have is something like:

(c1,c2).each {|x1,x2| .... }

I thought of writing my own iterator class so that I could do something
like:

Iterator.new(c1,c2).each {|x1,x2| .... }

but that looks clumsy and inefficient.

I am transitioning to using mostly Ruby (moving away from Java, Lisp,
and Smalltalk) and I would like to use the proper Ruby idioms.


class Array
def pairs
first.each_with_index{|a,i|
yield a, last
}
end
end

[%w(x y z), [2,4,6]].pairs{|a,b| print a,b,"\n" }
 
L

Logan Capaldo

--Apple-Mail-25-171560202
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
delsp=yes;
format=flowed


One way I'm fond of is:
require 'generator'
enum = SyncEnumerator.new([1,2,3], [7,8,9])
enum.each do |pair|
puts pair.inspect
end
# Results in:
[1, 7]
[2, 8]
[3, 9]

Warning, SyncEnumerator is slow, and you probably don't need it
since #zip is in enumerable.
Run the below for a demonstration. Original I had it run each
benchmark 10 times by the way, but I never had the patience to let
the syncenum versions finish:

% cat zip_vs_syncenum.rb
require 'benchmark'
require 'generator'
a = (1..100)
b = a.to_a.reverse

puts "Using zip:"
Benchmark.bm { |x|
x.report {
3.times { a.zip(b) { |x, y| z = x * y } }
}
}
puts "Using SyncEnumerator(new every time):"
Benchmark.bm { |x|
x.report {
3.times {
a_b_enum = SyncEnumerator.new(a, b)
a_b_enum.each { |x, y| z = x * y }
}
}
}

puts "Using SyncEnumerator(only one created):"
Benchmark.bm { |x|
x.report {
a_b_enum = SyncEnumerator.new(a, b)
3.times { a_b_enum.each { |x, y| z = x * y } }
}
}

__END__



--Apple-Mail-25-171560202--
 
W

William James

Hi --

Mark said:
If I have two containers c1 and c2 of the same length, what is the
proper "Ruby way" to do this:

c1.length.times {|i|
# access c1, c2
}

I like that Ruby container classes provide their own iterators, but
what I would like to have is something like:

(c1,c2).each {|x1,x2| .... }

I thought of writing my own iterator class so that I could do something
like:

Iterator.new(c1,c2).each {|x1,x2| .... }

but that looks clumsy and inefficient.

I am transitioning to using mostly Ruby (moving away from Java, Lisp,
and Smalltalk) and I would like to use the proper Ruby idioms.


foo = %w(x y z) ; bar = [2,4,6]
[foo, bar].transpose.each{|a,b| print a, b, $/ }
--->
x2
y4
z6


I don't think $/ is very idiomatic. See the ToDo file in the source;
it includes:

* discourage use of symbol variables (e.g. $/, etc.) in manual

:)


David


idiomatic, adj. Peculiar to a particular group or individual.

"\n" is found in C and in awk, but $/ isn't; so it is more
nearly peculiar to Ruby. Some may lack the capacity to remember
what it represents.

However, I have no doubt that this is an unfashionable opinion
and that your view is the dominant one.
 
D

dblack

Hi --

Hi --

Mark Watson wrote:
If I have two containers c1 and c2 of the same length, what is the
proper "Ruby way" to do this:

c1.length.times {|i|
# access c1, c2
}

I like that Ruby container classes provide their own iterators, but
what I would like to have is something like:

(c1,c2).each {|x1,x2| .... }

I thought of writing my own iterator class so that I could do something
like:

Iterator.new(c1,c2).each {|x1,x2| .... }

but that looks clumsy and inefficient.

I am transitioning to using mostly Ruby (moving away from Java, Lisp,
and Smalltalk) and I would like to use the proper Ruby idioms.

foo = %w(x y z) ; bar = [2,4,6]
[foo, bar].transpose.each{|a,b| print a, b, $/ }
--->
x2
y4
z6


I don't think $/ is very idiomatic. See the ToDo file in the source;
it includes:

* discourage use of symbol variables (e.g. $/, etc.) in manual

:)


David


idiomatic, adj. Peculiar to a particular group or individual.

"\n" is found in C and in awk, but $/ isn't; so it is more
nearly peculiar to Ruby. Some may lack the capacity to remember
what it represents.

However, I have no doubt that this is an unfashionable opinion
and that your view is the dominant one.


I share your opinion that $/ is not in C or awk, but is in Ruby :)
I'm thinking more about its position *within* Ruby, which isn't
directly connected to its presence or absence anywhere else. It's
there, but it seems to be in a bit of a shaky position.


David

--
David A. Black ([email protected])
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

"Ruby for Rails" chapters now available
from Manning Early Access Program! http://www.manning.com/books/black
 
J

James Edward Gray II

Warning, SyncEnumerator is slow...

It was recently reworked to use threads instead of continuations,
this resulted in a pretty significant speed boost. I doubt it beats
Enumerable#zip yet, but the speed will be nice.

James Edward Gray II
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top