Increasing counter whithin loop?


#1

Hi,

a very basic question…

I’d like to output the sequence “a b d e”, by testing if the current
element is == “b” then skip the next element and continue the loop. The
obvious solution doesn’t look rubyish to me, how could I use the first
or second attempt to get the desired solution?

Patrick

a=%w( a b c d e )

incorrect, outputs “a b c d e”

0.upto(a.size - 1) do |i|
puts a[i]
if a[i]==“b”
# skip next element
# but i won’t get affected
i += 1
end
end

incorrect, outputs “a b c d e”

for i in 0…a.size
puts a[i]
if a[i]==“b”
# skip next element
# but i won’t get affected
i += 1
end
end

incorrect, outputs nothing… is there a next_next ?

a.each do |elt|
puts elt
if elt==“b”
# skip next element
# ??
end
end

this one works, but is ugly

i=0
while i < a.size
puts a[i]
if a[i]==“b”
# skip next element
i += 1
end
i += 1
end


#2

a=%w( a b c d e )

seen = false

a.each {|elem|

unless seen
#do stuff
puts elem
seen = true if elem == “b”
else
seen = false
end
}

cheers,
Pavel


#3

Patrick G. wrote:


a=%w( a b c d e )

a=%w( a b c d e )
=> [“a”, “b”, “c”, “d”, “e”]

a.each do |elt|
?> next if elt==“b”

puts elt
end
a
c
d
e
=> [“a”, “b”, “c”, “d”, “e”]

Kind regards

robert

#4

Patrick G.:

I’d like to output the sequence “a b d e”, by testing if the current
element is == “b” then skip the next element and continue the loop.

I do not know whether I understood correctly what you want, but you
could
try this:

array = [ :a, :b, :c, :d, :e ]
array.each_with_index do |x, i|
next if array[i-1] == :b
print x, ’ ’
end

Malte


#5

“M” == Malte M. removed_email_address@domain.invalid writes:

try it, with

array = [ :a, :b, :c, :d, :e , :b]

M> array.each_with_index do |x, i|
M> next if array[i-1] == :b
M> print x, ’ ’
M> end

:slight_smile:

Guy Decoux


#6

Patrick G. wrote:

Hi,

a very basic question…

I’d like to output the sequence “a b d e”, by testing if the current
element is == “b” then skip the next element and continue the loop. The
obvious solution doesn’t look rubyish to me, how could I use the first
or second attempt to get the desired solution?

array = [1, :skip, :hidden, 2, :skip, :hidden, 3, :skip]

Zipped to ensure i is uniquely index()ed

Also makes sure use array non-destructively

zip = array.zip
for i in zip
if i[0] == :skip
zip.delete_at(zip.index(i) + 1)
else
puts i[0]
end
end

Output:

1
2
3


Neil S. - removed_email_address@domain.invalid

‘A republic, if you can keep it.’ – Benjamin Franklin


#7

ts:

try it, with

array = [ :a, :b, :c, :d, :e , :b]

Huh. Right. So it becomes

array.each_with_index do |x, i|
next if array[i-1] == :b unless i == 0
print x, ’ ’
end

Nasty.

Malte


#8

a = [“a”, “b”, “c”, “d”, “e”]

temp = nil

p a.select {|x| if temp == “b” then temp = nil; next end; temp = x;true}

outputs

[“a”, “b”, “d”, “e”]

Probably could be cleaner.


#9

On Dec 5, 2005, at 9:52 AM, Malte M. wrote:

ts:

try it, with

array = [ :a, :b, :c, :d, :e , :b]

Huh. Right. So it becomes

array.each_with_index do |x, i|
next if array[i-1] == :b unless i == 0

next if i > 0 and array[i - 1] == :b

print x, ’ ’
end

Nasty.

James Edward G. II


#10

Hello again,

thanks for the answers. I have been very unclear what I wanted, but
Malte, Guy and JEGII seemed to have read my mind.

a=%w( a b b d b e )

a.each_with_index do |x, i|
next if x == “b” and a[i - 1] == “b”
if x==“b” and a[i+1]==“b”
puts “double b”
else
puts x
end
end

(slightly different code example, but closer to what I wanted).

I wonder why I cannot change the variable in

for i in 0…x

end

Or am I missing some magic?

Patrick


#11

Patrick G. removed_email_address@domain.invalid wrote:

if x==“b” and a[i+1]==“b”
puts “double b”
else
puts x
end
end

I’d use Gary W.'s method, and cache the last seen element, rather
than rely upon the index (e.g. if you use a stream rather than an array,
you no longer have access to a[i-i]).

martin


#12

Maybe I missed something.

last = nil
%w( a b c d e ).select { |e| r = last != ‘b’ ; last = e ; r }

T.


#13

Maybe I’m just being thick, but wouldn’t it just be easier (and much
closer to what you are actually trying to do) to say

[“a”, “b”, “c”, “d”, “e”].each do |x|
puts x unless x == “c”
end

I’ll admit I’m also new to Ruby but isn’t the above the more “rubyish”
way to achieve what you’re trying to do?


#14

Quoting Patrick G. removed_email_address@domain.invalid:

output sequence[i]
end
end

would work, but I can’t change the i in the for-loop. This is a
pity!

It’s probably helpful to realize that Ruby has no for-loop in the
traditional sense. The above is equivalent to:

sequence.each do |i|
if sequence[i]==‘b’ and sequence[i+1]==‘b’
output ‘double b’
# increase i, so that the second ‘b’ won’t be seen by the
for-loop
# but ruby won’t let me!?
# i += 1 does nothing
else
output sequence[i]
end
end

Written this way, it’s probably more obvious why incrementing i
doesn’t do what you had expected.

-mental


#15

Maybe I missed something.

You missed to read my mind; my explanation was very unclear. I wanted
to have something like

“if two ‘b’ are consecutive, output ‘double b’ and continue with the
element behind the second”.

‘a’ ‘b’ ‘b’ ‘c’ ‘b’ ‘d’

->

‘a’ ‘double b’ ‘c’ ‘b’ ‘d’

This is why I thought

for i in sequence
if sequence[i]==‘b’ and sequence[i+1]==‘b’
output ‘double b’

increase i, so that the second ‘b’ won’t be seen by the for-loop

but ruby won’t let me!?

i += 1 does nothing

else
output sequence[i]
end
end

would work, but I can’t change the i in the for-loop. This is a pity!

Patrick


#16

It’s probably helpful to realize that Ruby has no for-loop in the
traditional sense. The above is equivalent to:

sequence.each do |i|
[…]
end

Written this way, it’s probably more obvious why incrementing i
doesn’t do what you had expected.

Yes, but when writing ‘for i in x … end’ I’d expect a for-loop :slight_smile:
It would be really nice to be able to increase the counter from within
the loop. I somewhat expected that to work, I can’t tell you why.
Perhaps that is what I was used to in other languages?

Patrick


#17

[…]

If you’re really tied to the traditional for loop, you can use a while loop
and do the increment yourself at the end of the loop, but usually you find a
better way to do it in Ruby that doesn’t involve going through the chars one
at a time, and that’s why we rarely use the for construct.

That is exactly what I am trying to find. I have a list (Array) of
different elements, which I want to render below each other, except
when there are two elements of type ‘b’, they can be put next to each
other. So I think I need a check like 'if this element is == ‘b’ and
next element is also == ‘b’, then render them next to each other. This
rendering has to be known in advance, so I can’t use information if
the last element is of type ‘b’ (with the second occurance of ‘b’).

Of course, I can write a while loop, but this would be

a) setting some counter to 0
b) accessing the elements via [] (index)
c) checking on counter <=> sequence.length

all which are acceptable, but don’t look like the nice ruby builtins
that I am used to. The

sequence.each do |element|

end

would be nice, but I understand that there is no
‘skip_the_next_element’-method. So the next nicer attempt would be

for counter in 0…element.size

increase_counter_by_one_to_skip_one_interation
end

But - contradicting my intuition - doesn’t seem to work/exist. So I
have to stick to an ugly while loop… :wink: So my question is: did I
miss something? Is there any reason why we can’t manipulate the
counter within the loop?

Patrick


#18

On Wednesday 07 December 2005 16:07, Patrick G. wrote:

other. So I think I need a check like 'if this element is == ‘b’ and
all which are acceptable, but don’t look like the nice ruby builtins

increase_counter_by_one_to_skip_one_interation
end

But - contradicting my intuition - doesn’t seem to work/exist. So I
have to stick to an ugly while loop… :wink: So my question is: did I
miss something? Is there any reason why we can’t manipulate the
counter within the loop?

No, you can manipulate the counter just fine, just that it won’t persist
for
the next iteration of the count. This is because ruby is providing the
i for
you, but not actually checking it to know where it is, or when it’s
done,
unlike similarly worded constructs in C etc.

How about something like:

irb(main):008:0> “abbcdeef”.gsub(/(\w)\1/) { |match|
irb(main):009:1* " double #{match[0, 1]} "
irb(main):010:1> }
=> “a double b cd double e f”


#19

Quoting Patrick G. removed_email_address@domain.invalid:

increase_counter_by_one_to_skip_one_interation
end

But - contradicting my intuition - doesn’t seem to work/exist. So
I have to stick to an ugly while loop… :wink: So my question is:
did I miss something?

There’s really no difference between:

for counter in 0…element_size

end

and

(0…element_size).each do |counter|

end

Both call Range#each with the given block.

Is there any reason why we can’t manipulate the counter within
the loop?

‘counter’ isn’t actually a counter. It’s just a parameter of the
block given to Range#each. While there’s probably a real counter
behind the scenes somewhere, it’s not exposed to you.

90% of the time you don’t need counters or while loops, though.
Even here, there’s nothing preventing you from doing e.g.:

skip = false
sequence.each do |element|
if skip
skip = false
next
end

set skip to true to skip the next iteration


end

or alternately:

skip = false
sequence.each do |element|
unless skip

# set skip to true to skip the next iteration

end
skip = false
end

-mental


#20

Hi Kevin,

[for i in … end ]

No, you can manipulate the counter just fine, just that it won’t persist for
the next iteration of the count. This is because ruby is providing the i for
you, but not actually checking it to know where it is, or when it’s done,
unlike similarly worded constructs in C etc.

That (last part of the sentence) is exactly what confused me. Nobody
else thinks that this persistance would be useful?

Patrick