Forum: Ruby map_if, collect_if ???

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Bruce W. (Guest)
on 2006-04-13 04:33
(Received via mailing list)
Hi,

I have been through the pickaxe book, looking at the array and
enumerable
built-in class and module and can't find a way of achieving a map_if or
collect_if.
From pickaxe, collect "Returns a new array containing the results of
running
block once for every element in enum". Of course map is an alias to
collect.
There is find_all but it returns an array containing the elements of
enum,
not the results of running the block, so I have written one.
Is there a better way that already exists?
thanks,
Bruce.

module Enumerable

  # returns an array containing the results of running block once for
every
element in enum, for which block
  # is not false.
  def map_if
    rt, r = [], nil
    self.each { |e| (r = yield(e)) and rt << r }
    rt
  end
  alias :collect_if :map_if

end

a = [ 1,2,3,4,5 ]

b = a.map_if { |i|
  (i % 2 != 0) and (i * 10)
}

p b ##  [10, 30, 50]
Mark Van H. (Guest)
on 2006-04-13 05:06
(Received via mailing list)
Try using the 'select' method...

irb(main):002:0* a = [ 1,2,3,4,5 ]
irb(main):003:0> b = a.select { |i|
irb(main):004:1*  (i % 2 != 0) and (i * 10)
irb(main):005:1> }
=> [1, 3, 5]
Logan C. (Guest)
on 2006-04-13 05:21
(Received via mailing list)
On Apr 12, 2006, at 9:04 PM, Mark Van H. wrote:

> Try using the 'select' method...
>
> irb(main):002:0* a = [ 1,2,3,4,5 ]
> irb(main):003:0> b = a.select { |i|
> irb(main):004:1*  (i % 2 != 0) and (i * 10)
> irb(main):005:1> }
> => [1, 3, 5]
>
>

Not the effect he was looking for:

b = a.map_if { |i| (i % 2 != 0) and (i * 10) }
p b
[10, 30, 50]

Personally I think his solution is a little icky, what if you wanted
to do

if x
   then map false

a.map_if  { |i| (i % 2 != 0) and false }
would break down and you'd get an empty array

I would just do

a.select { |i| (i % 2 != 0) }.map { |i| i * 10 }

if I needed map-if style functionality
unknown (Guest)
on 2006-04-13 07:56
(Received via mailing list)
On Thu, 13 Apr 2006, Bruce W. wrote:

> a = [ 1,2,3,4,5 ]
>
> b = a.map_if { |i|
>  (i % 2 != 0) and (i * 10)
> }
>
> p b ##  [10, 30, 50]

inject is very powerfull:

   harp:~ > cat a.rb
   a = 1 .. 5
   b = a.inject([]){|a,i| i[0] == 1 ? a.push(i * 10) : a}
   p b

   harp:~ > ruby a.rb
   [10, 30, 50]

regards.

-a
Robert D. (Guest)
on 2006-04-13 15:46
(Received via mailing list)
On 4/13/06, Bruce W. <removed_email_address@domain.invalid> wrote:
> There is find_all but it returns an array containing the elements of enum,
>   # is not false.
>
> b = a.map_if { |i|
>   (i % 2 != 0) and (i * 10)
> }
>
> p b ##  [10, 30, 50]
>
>
module Enumerable
    def map_if( &block )
        find_all(&block).map(&block)
    end
end

this for sure is shorter, had no time to test performance though. ( I
yield
twice, you yield only once, which might be costy!)

Cheers
Robert

--
Deux choses sont infinies : l'univers et la bêtise humaine ; en ce qui
concerne l'univers, je n'en ai pas acquis la certitude absolue.

- Albert Einstein
Bruce W. (Guest)
on 2006-04-13 16:02
(Received via mailing list)
Ara,

Thanks for your reply. I have been using inject but shyed away in this
case
because for the 'real' code that I have been working on I am actually
already using an accumualtor.

So I had;

acc = []
array.each { |v|
  condition_expression and acc << v.attribute
}

Using inject seems to be roughly equivalent. In the example above v is a
instance of the Struct class.

cheers,
Bruce.
Bruce W. (Guest)
on 2006-04-13 16:17
(Received via mailing list)
Robert and Logan,

Thanks for your replies. That was good thinking.

On 4/13/06, Robert D. <removed_email_address@domain.invalid> wrote:
>
> On 4/13/06, Bruce W. <removed_email_address@domain.invalid> wrote:
>
> module Enumerable
>     def map_if( &block )
>         find_all(&block).map(&block)
>     end
> end


I particularly like the use of calling block twice. At first I thought
that
this couldn't work but of course once the block has been successful for
the
call to find_all, it must be successful for the call to map -- very
nice.

I will file away the find_all  {}.map {} idiom for future use but for
this
case I will either use a manual accumulator or inject. The only reason
is
performance and performance only matters in this case because the code
is
likely to be get call many hundreds of times in a loop and I will be
impatiently waiting for it finish.

WRT to performance, not only is the block being called twice but an
extra
array is being created and then freed. In the past I have had issues
ruby
processes using a lot of memory and spending time with the garbage
collector. I don't want to start any kind of anti-ruby flame war here;
Ruby
is my first choice. Manually stopping the GC at the top of the loop and
starting the GC at the bottom of the loop, solved the problem but I just
want to minimse memory use where I can, just for this code.

thanks again.
Bruce.
Stephan M. (Guest)
on 2006-04-14 19:08
(Received via mailing list)
* removed_email_address@domain.invalid <removed_email_address@domain.invalid> 
[060413 06:04]:

>   harp:~ > cat a.rb
>   a = 1 .. 5
>   b = a.inject([]){|a,i| i[0] == 1 ? a.push(i * 10) : a}
>   p b
>
>   harp:~ > ruby a.rb
>   [10, 30, 50]

I don't get it: why is i[0] == 1 returning true in case i is an integer
with an odd value?


Thanks & cheers,

Steph.
Logan C. (Guest)
on 2006-04-14 19:17
(Received via mailing list)
On Apr 14, 2006, at 11:07 AM, Stephan M. wrote:

> I don't get it: why is i[0] == 1 returning true in case i is an
> integer
> with an odd value?
>
>
> Thanks & cheers,
>
> Steph.
>

For integers [] gives you the value of the bit at the given index.
All odd integers will have an LSB of 1
i.e.
1 -> 0b001
2 -> 0b010 # even
3 -> 0b011 #odd
4 -> 0b100 #even
5 -> 0b101 #odd
Stephan M. (Guest)
on 2006-04-15 23:16
(Received via mailing list)
* Logan C. <removed_email_address@domain.invalid> [060414 17:34]:

> For integers [] gives you the value of the bit at the given index. All odd integers will 
have an LSB of 1
> i.e.

Ah, I see. Thanks for your help!

Cheers,

Steph.
This topic is locked and can not be replied to.