Iterating with multiple values

Hi gurus and nubys,

Suppose you are dealing with an array like [1,2,3,4], and suppose ypou
want to deal, at any iteration, with 3 values.I mean something like:

[1,2,3,4].each_3 do |pre,cur,next|
print pre,cur,next,"\n"
end

nil,nil,1
nil,1,2
1,2,3
2,3,4
3,4,nil
4,nil,nil

What do you think is the best way to this? I can think of adding a
global index and use it to refer explicitly to an element oin the
array, but this is ugly to me…

Another way would be some kind of subversion of inject(), cause for
just two values it can be used to do what I want:
[1,2,3,4,5,6].inject do |pre1,cur|
print pre1,cur,"\n"
cur
end

I thought this should work:
[1,2,3,4,5,6].inject do |pre2,cur|
print pre2[0],pre2[1],cur,"\n"
[pre2[1],cur]
end

but it gives strange results in the first loops.
102
023
234
345
456

BTW, the point of this mail is: what do you think is the most rubyish
way to handle multiple values at a time ?

I would use Array#each_index and peek behind and ahead.

···

On Mar 12, 2004, at 11:09 AM, gabriele renzi wrote:

Hi gurus and nubys,

Suppose you are dealing with an array like [1,2,3,4], and suppose ypou
want to deal, at any iteration, with 3 values.I mean something like:

[1,2,3,4].each_3 do |pre,cur,next|
print pre,cur,next,“\n”
end

[gabriele renzi surrender_it@rc1.vip.ukl.yahoo.com, 2004-03-12 19.09 CET]

Hi gurus and nubys,

Suppose you are dealing with an array like [1,2,3,4], and suppose ypou
want to deal, at any iteration, with 3 values.I mean something like:

[1,2,3,4].each_3 do |pre,cur,next|
print pre,cur,next,“\n”
end

nil,nil,1
nil,1,2
1,2,3
2,3,4
3,4,nil
4,nil,nil

What do you think is the best way to this? I can think of adding a
global index and use it to refer explicitly to an element oin the
array, but this is ugly to me…
[…]

You can use an array to buffer intermediate results. Something like
(not tested):

module Enumerable
def each_n(n)
a = Array.new(n)
each do |cur|
a.shift
a.push cur
yield *a
end
(a.size-1).times do
a.shift
a.push nil
yield *a
end
end
end

Good luck.

gabriele renzi wrote:

Suppose you are dealing with an array like [1,2,3,4], and suppose ypou
want to deal, at any iteration, with 3 values.I mean something like:

[1,2,3,4].each_3 do |pre,cur,next|
print pre,cur,next,“\n”
end

nil,nil,1
nil,1,2
1,2,3
2,3,4
3,4,nil
4,nil,nil

What do you think is the best way to this? I can think of adding a
global index and use it to refer explicitly to an element oin the
array, but this is ugly to me…

You can also use the enumerator extension.
The output differs from yours but possibly
satisfies your ultimate desires anyway

require ‘enumerator’

(1…9).each_cons(3) do |a,b,c|
print a,b,c,“\n”
end

···

123
234
345
456
567
678
789

/Christoph

to get an idea of why it doesn’t work, try this:

[1,2,3,4,5,6].inject do |pre2,cur|
print pre2[0],pre2[1],cur," :: “,pre2.inspect,cur.inspect,”\n"
[pre2[1],cur]
end

output:
102 :: 12
023 :: [0, 2]3
234 :: [2, 3]4
345 :: [3, 4]5
456 :: [4, 5]6

pre2 is not an array on the first pass. Also, unless you pass an
argument to #inject, on the first loop, it passes in the first two
values. Try this:

[1,2,3,4].inject([nil]*2) do |pre,cur|
puts “%s,%s,%s” % (pre + [cur])
pre.slice(1…-1) + [cur]
end

,1
,1,2
1,2,3
2,3,4

If you pass an array of nils, it works the way you might expect. Also,
this is scalable; you can multiply [nil] by a larger number to capture
more previous variables:

(1…8).to_a.inject([nil]*4) do |pre,cur|
puts “%s,%s,%s,%s,%s” % (pre + [cur])
pre.slice(1…-1) + [cur]
end

,1
,1,2
,1,2,3
,1,2,3,4
1,2,3,4,5
2,3,4,5,6
3,4,5,6,7
4,5,6,7,8

–Mark

···

On Mar 12, 2004, at 10:09 AM, gabriele renzi wrote:

I thought this should work:
[1,2,3,4,5,6].inject do |pre2,cur|
print pre2[0],pre2[1],cur,“\n”
[pre2[1],cur]
end

but it gives strange results in the first loops.
102
023
234
345
456

gabriele renzi wrote:

Hi gurus and nubys,

Suppose you are dealing with an array like [1,2,3,4], and suppose ypou
want to deal, at any iteration, with 3 values.I mean something like:

[1,2,3,4].each_3 do |pre,cur,next|
print pre,cur,next,“\n”
end

nil,nil,1
nil,1,2
1,2,3
2,3,4
3,4,nil
4,nil,nil

The enum tools library[1] has some methods for doing this:

require ‘enum/cluster’

(0…5).each_with_neighbors { |x| p x }

prints:

[nil, 0, 1]

[0, 1, 2]

[1, 2, 3]

[2, 3, 4]

[3, 4, 5]

[4, 5, nil]

[1,2,3,4].each_with_neighbors(8, 0) { |x| p x }

prints:

[0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, 0]

[0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0]

[0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0]

[0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0]

(‘a’…‘g’).each_cluster(5) { |x| p x.join ‘’ }

prints:

“abcde”

“bcdef”

“cdefg”

[1] http://raa.ruby-lang.org/list.rhtml?name=enum

gabriele renzi schrieb:

Hi gurus and nubys,

Moin!

Suppose you are dealing with an array like [1,2,3,4], and suppose ypou
want to deal, at any iteration, with 3 values.I mean something like:

[1,2,3,4].each_3 do |pre,cur,next|
print pre,cur,next,“\n”
end

nil,nil,1
nil,1,2
1,2,3
2,3,4
3,4,nil
4,nil,nil

I’ve implemented this like this:

module Enumerable
def each_window(window)
window = 0 … window unless window.is_a? Range
window_end = window.end + (window.exclude_end? ? 0 : 1)
elements, pushed = , 0
each_with_index { |item, index|
elements.push(item)
pushed += 1
if pushed % window_end == 0
start = [0, window.begin - window_end + pushed].max
finish = [0, window_end + pushed].max
yield(elements[start … finish])
end
}
end
end

You can use it like this for your example:

[1, 2, 3, 4].each_window(-2…0) { |items| p items }
[1]
[1, 2]
[1, 2, 3]
[2, 3, 4]

It’s a bit different from your example however. (It won’t yield trailing
nils.)

It can also be used to iterate groups of three elements like this:

[1, 2, 3, 4, 5, 6].each_window(3) { |items| p items }

Or you can skip elements like this:

[“foo”, 1, 2, “bar”, 3, 4].each_window(1…2) { |items| p items }

Maybe it is at least of some use to you.

Regards,
Florian Gross

wow, that’s exactly what I was looking for, I did’nt know enumerator
did this.
Thank you and thanks to Gavin, Carlos and Mark.

···

il Fri, 12 Mar 2004 21:25:35 +0100, Christoph chr_mail@gmx.net ha scritto::

You can also use the enumerator extension.
The output differs from yours but possibly
satisfies your ultimate desires anyway

require ‘enumerator’

(1…9).each_cons(3) do |a,b,c|
print a,b,c,“\n”
end


123
234
345
456
567
678
789

/Christoph