How about Array#collect_until

I am wondering if anyone has implemented an Array#collect_until method
to break out of a collect loop after an end-point has been achieved
during an Array#collect call.

For instance, you have a long array of items and want to use each item
to calculate something, but should the calculation give a specific
result, then you know you are done--no need for processing the rest of
the array. However, if that specified result is not met, the
processing continues. Something like below:

[1,2,3].collect{|item| item + 10} # => [11,12,13]
[1,2,3].collect_until({|x| x%2 == 0}) {|item| item + 10} # => [11,12]

To get this to work the result from the yield to {|item| item + 10}
block would be run through the {|x| x%2 == 0} block to check if we are
done.

Thanks for any suggestions.

I am wondering if anyone has implemented an Array#collect_until method
to break out of a collect loop after an end-point has been achieved
during an Array#collect call.

For instance, you have a long array of items and want to use each item
to calculate something, but should the calculation give a specific
result, then you know you are done--no need for processing the rest of
the array. However, if that specified result is not met, the
processing continues. Something like below:

[1,2,3].collect{|item| item + 10} # => [11,12,13]
[1,2,3].collect_until({|x| x%2 == 0}) {|item| item + 10} # => [11,12]

This would not work - it does not even parse

irb(main):004:0> 09:04:52 ~$ ruby19 -ce '[1,2,3].collect_until({|x|
x%2 == 0}) {|item| item + 10}'
-e:1: syntax error, unexpected '|', expecting '}'
[1,2,3].collect_until({|x| x%2 == 0}) {|item| item + 10}
                        ^
-e:1: syntax error, unexpected '}', expecting $end
[1,2,3].collect_until({|x| x%2 == 0}) {|item| item + 10}
                                    ^
09:04:59 ~$

You could at least have to use 'lambda' or 'proc':

09:04:59 ~$ ruby19 -ce '[1,2,3].collect_until(lambda {|x| x%2 == 0})
{|item| item + 10}'
Syntax OK

To get this to work the result from the yield to {|item| item + 10}
block would be run through the {|x| x%2 == 0} block to check if we are
done.

You can use #inject for this:

irb(main):002:0> [1,2,3].inject(){|a,x| a << (x+10); break a if x % 2 == 0;a}
=> [11, 12]

It also works if the condition never fires:

irb(main):003:0> [1,3,5].inject(){|a,x| a << (x+10); break a if x % 2 == 0;a}
=> [11, 13, 15]

Kind regards

robert

···

2010/8/27 timr <timrandg@gmail.com>:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Here is an awful hacked solution...perhaps someone come up with a
prettier solution

class Array
  def try(something)
    contents = self.dup
    while contents.length != 0
      item = contents.shift
      result = yield(item)
      if something.call(result)
        p result
        break
      else
        p result
      end
    end
  end
end

%w[ find out if that this but not the other mother
mouth].try(Proc.new {|word| word[-2,2] == 'st' }){|item| item + "t" }

puts "another attempt"

[1,6,7,5,3,0,10,12,14].try(Proc.new {|num| num > 25 }){|item| item *
4 }

puts "another attempt"

[1,2,3].try(Proc.new {|x| x%2 == 0}) {|item| item + 10}

RESULTS:

"findt"
"outt"
"ift"
"thatt"
"thist"
another attempt
4
24
28
another attempt
11
12

Hi,

···

In message "Re: how about Array#collect_until" on Fri, 27 Aug 2010 15:32:29 +0900, timr <timrandg@gmail.com> writes:

[1,2,3].collect{|item| item + 10} # => [11,12,13]
[1,2,3].collect_until({|x| x%2 == 0}) {|item| item + 10} # => [11,12]

How about using

[1,2,3].take_while{|x| x%2 != 0}.collect{|item| item + 10}

? Ah, collect_until seems inclusive, but I think it shouldn't.

              matz.

You can't pass an anonymous function using the syntax you're using. You can pass it as a lambda, something like:

[1,2,3].collect_until(lambda {|x| x%2 == 0}) {|item| item + 10}

You could define collect_until like this:

class Array
  def collect_until(fn, &block)
    result =
    self.each do |x|
      if !fn.call(x)
        result << (yield x)
      else
        break
      end
    end
    result
  end
end

I think the actual result would be only [11], since the collection would proceed UNTIL the value mod 2 was zero, which would happen at 2. Therefore the processing would stop then.

steven

···

On Aug 27, 2010, at 12:32 AM, timr wrote:

I am wondering if anyone has implemented an Array#collect_until method
to break out of a collect loop after an end-point has been achieved
during an Array#collect call.

For instance, you have a long array of items and want to use each item
to calculate something, but should the calculation give a specific
result, then you know you are done--no need for processing the rest of
the array. However, if that specified result is not met, the
processing continues. Something like below:

[1,2,3].collect{|item| item + 10} # => [11,12,13]
[1,2,3].collect_until({|x| x%2 == 0}) {|item| item + 10} # => [11,12]

To get this to work the result from the yield to {|item| item + 10}
block would be run through the {|x| x%2 == 0} block to check if we are
done.

Thanks for any suggestions.

[1,2,3].collect{|item| item + 10} # => [11,12,13]
[1,2,3].collect_until({|x| x%2 == 0}) {|item| item + 10} # => [11,12]

Why not something like this :

class Array
  def collect_until
    n =
    self.each do |e|
      r, brk = yield e
      n << r
      break if brk
    end
    n
  end
end

Used like this :

[1,2,3].collect_until { |x| [ x + 10 , (x % 2 == 0) ] }

=> [11, 12]

Not very pretty, but it does the job.

Fred

···

Le 27 août à 08:29, timr a écrit :
--
Actual dialogue from the end of the battle:
Cleric: "Keep hitting it!"
Paladin: "It's dead."
Fighter: "Yeah, but it's not dead enough." (David P. Murphy in the SDM)

I know you already got something that works, but I think this is a
problem where enumerators provide an elegant solution:

module Enumerable
  def through
    Enumerator.new do |yielder|
      self.each do |val|
        yielder.yield(val)
        break if yield val
      end
    end
  end
end

then:

[1,2,3].through {|x| x % 2 == 0}.collect {|item| item+10} => [11,12]

···

On Thu, Aug 26, 2010 at 11:32 PM, timr <timrandg@gmail.com> wrote:

I am wondering if anyone has implemented an Array#collect_until method
to break out of a collect loop after an end-point has been achieved
during an Array#collect call.

For instance, you have a long array of items and want to use each item
to calculate something, but should the calculation give a specific
result, then you know you are done--no need for processing the rest of
the array. However, if that specified result is not met, the
processing continues. Something like below:

[1,2,3].collect{|item| item + 10} # => [11,12,13]
[1,2,3].collect_until({|x| x%2 == 0}) {|item| item + 10} # => [11,12]

To get this to work the result from the yield to {|item| item + 10}
block would be run through the {|x| x%2 == 0} block to check if we are
done.

Thanks for any suggestions.

I think he was thinking of something more like collect_unless, a combination of filtering and collecting.

steven

···

On Aug 27, 2010, at 1:24 AM, Yukihiro Matsumoto wrote:

? Ah, collect_until seems inclusive, but I think it shouldn't.

Thanks everyone for the great ideas. I ended up slightly modifying
Senault's suggestion (see below), though I learned from all the
various approaches. In my particular case I do need an inclusive
collect (long story, but using this to do some DNA fuzzy expression
matching that smartly knows when it has found a good solution and
breaks rather than finishing all the other possible calculations, and
I need to see the solution). And though the inject is a great
suggestion, I would prefer keeping the code blocks for determining
endpoint grammatically separated from the processing block, rather
than embedded.
Arigato Ruby Community,
Tim

#collects items processed through block until a solution satisfies the
function test (lambda{})
class Array
  def collect_until(fn, &block)
    results =
    self.each do |x|
      result = (yield x)
      results << result
      break if fn.call(result)
    end
    results
  end
end
p %w[ find out if that this but not the other mother
mouth].collect_until(lambda {|word| word[-2,2] == 'st' }){|item| item
+ "t" }

puts "another attempt"

p [1,6,7,5,3,0,10,12,14].collect_until(Proc.new {|num| num > 25 }){|
item> item * 4 }

puts "another attempt"
p [1,2,3].collect_until(Proc.new {|x| x%2 == 0}) {|item| item + 10}

···

On Aug 27, 1:47 am, "F. Senault" <f...@lacave.net> wrote:

Le 27 août à 08:29, timr a écrit :

> [1,2,3].collect{|item| item + 10} # => [11,12,13]
> [1,2,3].collect_until({|x| x%2 == 0}) {|item| item + 10} # => [11,12]

Why not something like this :

class Array
defcollect_until
n =
self.each do |e|
r, brk = yield e
n << r
break if brk
end
n
end
end

Used like this :

>> [1,2,3].collect_until{ |x| [ x + 10 , (x % 2 == 0) ] }

=> [11, 12]

Not very pretty, but it does the job.

Fred
--
Actual dialogue from the end of the battle:
Cleric: "Keep hitting it!"
Paladin: "It's dead."
Fighter: "Yeah, but it's not dead enough." (David P. Murphy in the SDM)

nice. i'd add that to my lib if you don't mind :wink:
now i can do eg,
[1,2,3].through{|x| x%2==0}.with_index.map{|x,i| [x,i,x+i]}
  #=> [[1, 0, 1], [2, 1, 3]]

i wonder why ruby's take_while defaulted to array when in blocked form...

thanks and best regards -botp

···

On Sat, Aug 28, 2010 at 4:15 AM, Christopher Dicely <cmdicely@gmail.com> wrote:

module Enumerable
def through
Enumerator.new do |yielder|
self.each do |val|
yielder.yield(val)
break if yield val
end
end
end
end

I know you already got something that works, but I think this is a
problem where enumerators provide an elegant solution:

module Enumerable
def through
Enumerator.new do |yielder|
self.each do |val|
yielder.yield(val)
break if yield val
end
end
end
end

then:

[1,2,3].through {|x| x % 2 == 0}.collect {|item| item+10} => [11,12]

Thanks for another useful answer.

I need to read the doc for Enumerator class since I don't really know
how to use it, but your solution appears to run each item into the
first block, until it finds an item producing a true result and then
everything up to that point is sent on to a collect statement for
processing in the second block. I am sure this is going to be useful
in many cases, but it is different from what I was intending for the
collect_until function. You will see how our two solutions differ if
you look at the test cases and the results below.

%w[ find out if that this but not the other mother
mouth].collect_until(lambda {|word| word[-2,2] == 'st' }){|item| item
+ "t" } # => ["findt", "outt", "ift", "thatt", "thist"]

[1,6,7,5,3,0,10,12,14].collect_until(Proc.new {|num| num > 25 }){|
item> item * 4 } # => [4, 24, 28]

[1,2,3].collect_until(Proc.new {|x| x%2 == 0}) {|item| item + 10} # =>
[11, 12]

Versus:
%w[ find out if that this but not the other mother mouth].through {|
word> word[-2,2] == 'st' }.collect{|item| item + "t" } # => ["findt",
"outt", "ift", "thatt", "thist", "butt", "nott", "thet", "othert",
"mothert", "moutht"]

[1,6,7,5,3,0,10,12,14].through {|num| num > 25 }.collect{|item| item *
4 } # => [4, 24, 28, 20, 12, 0, 40, 48, 56]

[1,2,3].through {|x| x%2 == 0}.collect{|item| item + 10} # => [11, 12]

Thanks everyone for the great ideas. I ended up slightly modifying
Senault's suggestion (see below), though I learned from all the
various approaches. In my particular case I do need an inclusive
collect (long story, but using this to do some DNA fuzzy expression
matching that smartly knows when it has found a good solution and
breaks rather than finishing all the other possible calculations, and
I need to see the solution). And though the inject is a great
suggestion, I would prefer keeping the code blocks for determining
endpoint grammatically separated from the processing block, rather
than embedded.
Arigato Ruby Community,
Tim

#collects items processed through block until a solution satisfies the
function test (lambda{})
class Array

I would rather place this code in Enumerable. Then all other classes mixing in this module will benefit.

   def collect_until(fn,&block)
     results =
     self.each do |x|

Why does everybody write "self.each" when "each" is sufficient?

       result = (yield x)
       results<< result
       break if fn.call(result)

As far as I can see this is not in line with your original requirement because in your original posting you evaluated the termination condition on the original value while this code does it on the result of the mapping calculation.

If you change that you don't need to store "result". You can also use "return" instead of "break" here for a more immediate exit:

return results if fc

     end
     results
   end
end
p %w[ find out if that this but not the other mother
mouth].collect_until(lambda {|word| word[-2,2] == 'st' }){|item| item
+ "t" }

puts "another attempt"

p [1,6,7,5,3,0,10,12,14].collect_until(Proc.new {|num| num> 25 }){|
item> item * 4 }

puts "another attempt"
p [1,2,3].collect_until(Proc.new {|x| x%2 == 0}) {|item| item + 10}

Kind regards

  robert

···

On 27.08.2010 21:08, timr wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

I would rather place this code in Enumerable. Then all other classes
mixing in this module will benefit.

Good idea.

> def collect_until(fn,&block)
> results =
> self.each do |x|

Why does everybody write "self.each" when "each" is sufficient?

self.sorry, er, I mean sorry. No you're right. It is overly explicit.

> result = (yield x)
> results<< result
> break if fn.call(result)

As far as I can see this is not in line with your original requirement
because in your original posting you evaluated the termination condition
on the original value while this code does it on the result of the
mapping calculation.

From the original posting:
[1,2,3].collect_until({|x| x%2 == 0}) {|item| item + 10} # => [11,12]
To get this to work the result from the yield to {|item| item + 10}
block would be run through the {|x| x%2 == 0} block to check if we
are
done.

So, I was shooting for a method that would send to block, take the
result and check it against fn and quit if condition was met. With the
call made as .collect_until(fn,&block). I am trying to save
processing from sending any more items than are required to a
calculation intensive block.

The other tests may help clarify the expected behavior. In each case
the method should result in a collection that DOES NOT INCLUDE A
RESULT FOR EVERY ITEM in the original set since the condition is met
part way through and break is executed. Therefore, it provides the
most a collection of calculations upto and including the solution when
an end condition was met.

···

> p %w[ find out if that this but not the other mother
> mouth].collect_until(lambda {|word| word[-2,2] == 'st' }){|item| item
> + "t" }

> puts "another attempt"

> p [1,6,7,5,3,0,10,12,14].collect_until(Proc.new {|num| num> 25 }){|
> item> item * 4 }

> puts "another attempt"
> p [1,2,3].collect_until(Proc.new {|x| x%2 == 0}) {|item| item + 10}

Kind regards

    robert

--
remember.guy do |as, often| as.you_can - without endhttp://blog.rubybestpractices.com/

Okay.. I misunderstood the intent. So you can't use the
Enumerable#through method I proposed upthread quite as simply, because
you have to reverse the order of the filtering and transforming
operations, and the existing transforming operation for enumerables
(Enumerable#map) produces an array, not an Enumerator, and
Kernel#enum_for won't pass a block to map (or any other method) to
create an Enumerator that uses the block.

But its easy to create a general-purpose map-like enumerator method.

module Enumerable
  def transform
    Enumerator.new do |yielder|
      self.each { |val| yielder << yield(val) }
    end
  end
end

Then you can just chain this Enumerable#transform in front of the
Enumerable#through I proposed upthread,

···

On Fri, Aug 27, 2010 at 9:40 PM, timr <timrandg@gmail.com> wrote:

The other tests may help clarify the expected behavior. In each case
the method should result in a collection that DOES NOT INCLUDE A
RESULT FOR EVERY ITEM in the original set since the condition is met
part way through and break is executed. Therefore, it provides the
most a collection of calculations upto and including the solution when
an end condition was met.