Iterate with condition

Hi,

I need but actually don't find how to iterate an iterable collection in Java-way. It seems generally like this:

Iterator it = objs.iterator();
while (it.hasMore()) {
     Object anObj = it.next();
     ...
}

How can I do it in Ruby? I know objs.each do |anObj| ... end but it's not (enough) good for me. I want to use additional cycle condition like this:

boolean b = true;
Iterator it = objs.iterator();
while (b && it.hasMore()) {
     Object anObj = it.next();
     ...
}

What is the equivalent to this in Ruby?
Thanks,

     Gábor

By some lingo "Java stlyle" iterators are called "external", "Ruby
style" ones are called "internal". I make this note just to separate
languages from concepts...

So, Ruby has built-in support for only for internal iterators, but ruby
also features callcc, which makes it easy to create external ones, too.

The generator package (part of the standard lib) implements this
concept.

While this approach is pretty elegant, the downside is a massive
performance loss.

Thus you might consider using a dumb solution, if there is one. Eg, a
typical case is when you want to iterate through two arrays
simultaneously -- then, unless you have huge arrays, it would be faster
to create a joint array by Array#zip or Array#transpose, and use its
each method, or just dup the arrays and shfit them.

Csaba

···

On Mon, Jun 20, 2005 at 07:52:30PM +0900, G?bor SEBESTY?N wrote:

Hi,

I need but actually don't find how to iterate an iterable collection
in Java-way. It seems generally like this:

Iterator it = objs.iterator();
while (it.hasMore()) {
    Object anObj = it.next();
    ...
}

What is the equivalent to this in Ruby?

Gábor SEBESTYÉN wrote:

How can I do it in Ruby? I know objs.each do |anObj| ... end but it's not (enough) good for me. I want to use additional cycle condition like this:

boolean b = true;
Iterator it = objs.iterator();
while (b && it.hasMore()) {
    Object anObj = it.next();
    ...
}

The break statement terminates a loop.

b = true
foo.each { b = do_something(); break unless b}

Gábor SEBESTYÉN wrote:

Hi,

[..]

boolean b = true;
Iterator it = objs.iterator();
while (b && it.hasMore()) {
    Object anObj = it.next();
    ...
}

What is the equivalent to this in Ruby?

You can abuse Enumerable#find for this:

objs.find do |obj|
  # do something with obj

  !b # return true to stop
end

I don't know wether is very rubyish :wink:

Thanks,

    Gábor

Sebastian

Timothy Hunter wrote:

Gábor SEBESTYÉN wrote:

How can I do it in Ruby? I know objs.each do |anObj| ... end but it's
not (enough) good for me. I want to use additional cycle condition
like this:

boolean b = true;
Iterator it = objs.iterator();
while (b && it.hasMore()) {
    Object anObj = it.next();
    ...
}

The break statement terminates a loop.

b = true
foo.each { b = do_something(); break unless b}

return works as well if in a method

def sample
  foo.each {|x| return x if do_something(x) }
end

There are other methods that may suit depending on Gabor's requirements:
#find, #select, #any?, #all? ...

What exactly do you want to do with the collection?

Kind regards

    robert

i think you mean #find_all

I want to get the first N pieces of "valid" objects from a collection. I would iterate in collection counting how many objects were sucessfully accepted and would break the cycle if there are no more objects or I already have my N objects. Here's my solution (written for Rails and the collection consists of ActiveRecord objects):

         cl = CustomerQueue.find(:all, :conditions => cond, :order => "created_at ASC")

         i = 0
         while i < cl.size and n > 0
             c = _openChat(cl[i].customer_id, @session[:user].id)
             unless c.nil?
                 chats << c
                 n = n - 1
             end
         end

I don't like referencing by index. That's why I asked you how to implement this using iterator and not a simple while cycle with indexing.

Gábor

"Never trust a computer you can't throw out a window." - Steve Wozniak

···

On 2005.06.20., at 14:10, Robert Klemme wrote:

What exactly do you want to do with the collection?

Gene Tani wrote:

i think you mean #find_all

No, I meant #find. #find_all always iterates all elements, so it's as
(un)usable as #each in this case.

Sebastian

Maybe

cl.each_with_index { |e,i|
   i < n or break
   c = _openChat(e.customer_id, @session[:user].id) and chats << c
}

Csaba

···

On Mon, Jun 20, 2005 at 11:31:09PM +0900, G?bor SEBESTY?N wrote:

On 2005.06.20., at 14:10, Robert Klemme wrote:

>What exactly do you want to do with the collection?
>
I want to get the first N pieces of "valid" objects from a
collection. I would iterate in collection counting how many objects
were sucessfully accepted and would break the cycle if there are no
more objects or I already have my N objects. Here's my solution
(written for Rails and the collection consists of ActiveRecord objects):

        cl = CustomerQueue.find(:all, :conditions => cond, :order =>
"created_at ASC")

        i = 0
        while i < cl.size and n > 0
            c = _openChat(cl[i].customer_id, @session[:user].id)
            unless c.nil?
                chats << c
                n = n - 1
            end
        end

I don't like referencing by index. That's why I asked you how to
implement this using iterator and not a simple while cycle with
indexing.

Csaba Henk wrote:

What exactly do you want to do with the collection?

I want to get the first N pieces of "valid" objects from a
collection. I would iterate in collection counting how many objects
were sucessfully accepted and would break the cycle if there are no
more objects or I already have my N objects. Here's my solution
(written for Rails and the collection consists of ActiveRecord
objects):

        cl = CustomerQueue.find(:all, :conditions => cond, :order =>
"created_at ASC")

        i = 0
        while i < cl.size and n > 0
            c = _openChat(cl[i].customer_id, @session[:user].id)
            unless c.nil?
                chats << c
                n = n - 1
            end
        end

I don't like referencing by index. That's why I asked you how to
implement this using iterator and not a simple while cycle with
indexing.

Maybe

cl.each_with_index { |e,i|
   i < n or break
   c = _openChat(e.customer_id, @session[:user].id) and chats << c
}

Nah, this cries for #inject!

found = collection.inject() do |f,x|
  if check_condition(x)
    f << x
    break f if f.size >= 10
  end
  f
end

(0..100).inject(){|f,x| if x%3==0 then f << x; break f if f.size ==

10 end; f}
=> [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]

(0..10).inject(){|f,x| if x%3==0 then f << x; break f if f.size == 10

end; f}
=> [0, 3, 6, 9]

:slight_smile:

Kind regards

    robert

···

On Mon, Jun 20, 2005 at 11:31:09PM +0900, G?bor SEBESTY?N wrote:

On 2005.06.20., at 14:10, Robert Klemme wrote:

I see, you are right -- the counter should tick only if a "non-void"
entry is fetched.

Csaba

···

On Tue, Jun 21, 2005 at 12:00:36AM +0900, Robert Klemme wrote:

Csaba Henk wrote:
> Maybe
>
> cl.each_with_index { |e,i|
> i < n or break
> c = _openChat(e.customer_id, @session[:user].id) and chats << c
> }

Nah, this cries for #inject!

found = collection.inject() do |f,x|
  if check_condition(x)
    f << x
    break f if f.size >= 10
  end
  f
end

Wow! Perfect!
Thaaaanks! :slight_smile:

     Gábor

To be is to do. - Socrates
To do is to be. - Sartre
Do be do be do. - Sinatra

···

On 2005.06.20., at 17:00, Robert Klemme wrote:

Nah, this cries for #inject!