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.
8cfdf47d27893fbd55d6b9565c34fbe4?d=identicon&s=25 Bruce Woodward (Guest)
on 2006-04-13 02: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]
3bef3fc4d0cbcf58f3d66c106e2fd16f?d=identicon&s=25 Mark Van Holstyn (Guest)
on 2006-04-13 03: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]
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2006-04-13 03:21
(Received via mailing list)
On Apr 12, 2006, at 9:04 PM, Mark Van Holstyn 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
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-04-13 05:56
(Received via mailing list)
On Thu, 13 Apr 2006, Bruce Woodward 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
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2006-04-13 13:46
(Received via mailing list)
On 4/13/06, Bruce Woodward <bruce.woodward@gmail.com> 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
8cfdf47d27893fbd55d6b9565c34fbe4?d=identicon&s=25 Bruce Woodward (Guest)
on 2006-04-13 14: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.
8cfdf47d27893fbd55d6b9565c34fbe4?d=identicon&s=25 Bruce Woodward (Guest)
on 2006-04-13 14:17
(Received via mailing list)
Robert and Logan,

Thanks for your replies. That was good thinking.

On 4/13/06, Robert Dober <robert.dober@gmail.com> wrote:
>
> On 4/13/06, Bruce Woodward <bruce.woodward@gmail.com> 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.
Fcc5cdf0f0f3e1a3a39c11ed4bf8d5e5?d=identicon&s=25 Stephan Mueller (Guest)
on 2006-04-14 17:08
(Received via mailing list)
* ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> [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.
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2006-04-14 17:17
(Received via mailing list)
On Apr 14, 2006, at 11:07 AM, Stephan Mueller 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
Fcc5cdf0f0f3e1a3a39c11ed4bf8d5e5?d=identicon&s=25 Stephan Mueller (Guest)
on 2006-04-15 21:16
(Received via mailing list)
* Logan Capaldo <logancapaldo@gmail.com> [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.