# efficient collect+compact ?

Discussion in 'Ruby' started by Martin Pirker, Apr 21, 2004.

1. ### Martin PirkerGuest

Hello...

What's the most efficient way for

puts [1,2,3,4,5,6,7,8,9,10].collect{ |x| next if x % 2 == 0; x+10 }.compact
or
puts [1,2,3,4,5,6,7,8,9,10].collect!{ |x| next if x % 2 == 0; x+10 }.compact!

Or:
I want to iterate over all array elements, process them, decide
whether the result is any good, and keep only the good ones.
It's ok doing that by chaining collect+compact, but I ponder this must
be slow/inefficient for array with xxxxx elements.

I read Array and Enumerable docs, have I missed something[1] or does
a straight "collect everything but nil" function not exist?

Martin

[1]
(it's already past 3am but I remember searching the same thing sometime ago)

Martin Pirker, Apr 21, 2004

2. ### Florian FrankGuest

This is perhaps better:

[1,2,3,4,5,6,7,8,9,10].inject([]) { |s, x| x % 2 == 0 ? s : s << x + 10 }

At least the created temporary array is smaller.

Florian Frank, Apr 21, 2004

3. ### Ara.T.HowardGuest

doesn't seem to make much difference:

~ > time ruby -e '(1..ARGV.shift.to_i).to_a.collect{|x| next if x % 2 == 0; x+10 }.compact' 1048576

real 0m1.496s
user 0m1.480s
sys 0m0.020s

~ > time ruby -e '(1..ARGV.shift.to_i).to_a.collect!{ |x| next if x % 2 == 0; x+10 }.compact!' 1048576

real 0m1.396s
user 0m1.390s
sys 0m0.000s

suprising.
-a
--
===============================================================================
| EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
| PHONE :: 303.497.6469
| ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
| URL :: http://www.ngdc.noaa.gov/stp/
| TRY :: for l in ruby perl;do \$l -e "print \"\x3a\x2d\x29\x0a\"";done
===============================================================================

Ara.T.Howard, Apr 21, 2004
4. ### Chris DuttonGuest

Hmmm... not sure I can offer any help in increasing performance, but it
seems perhaps the following is a bit cleaner:

puts (1..10).to_a.collect{ |x| x + 10 if x % 2 == 0 }.compact

Chris Dutton, Apr 21, 2004
5. ### gabriele renziGuest

try yourself:

map!+compact! 11
0.141000 0.000000 0.141000 ( 0.141000)
map+compact 21
0.078000 0.000000 0.078000 ( 0.078000)
inject 21
0.235000 0.000000 0.235000 ( 0.234000)
external 21
0.093000 0.000000 0.093000 ( 0.094000)
external for 21
0.094000 0.000000 0.094000 ( 0.094000)

# code
require 'benchmark'
Z=(1..100000).to_a
Benchmark.bm(10) do |b|
b.report 'map!+compact! ' do
ary=Z.map!{ |x| next if x % 2 == 0; x+10 }.compact!
p ary[0]
end
b.report 'map+compact ' do
ary=Z.map{ |x| next if x % 2 == 0; x+10 }.compact
p ary[0]
end
b.report 'inject ' do
ary=Z.inject([]) { |s, x| x % 2 == 0 ? s : s << x + 10 }
p ary[0]
end
b.report 'external ' do
ary=[]
Z.each {|x| ary<<(x+10) unless x%2==0 }
p ary[0]
end
b.report 'external for ' do
ary=[]
for i in Z
ary<<(i+10) unless i%2==0
end
p ary[0]
end
end

gabriele renzi, Apr 21, 2004
6. ### Kaspar SchiessGuest

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Martin Pirker wrote:

| I read Array and Enumerable docs, have I missed something[1] or does
| a straight "collect everything but nil" function not exist?

The function select collects everything but false elements, the function
reject does the opposite. I am surprised that this did not come up as

(insert this into code posted earlier on thread)
b.report 'select+collect' do
~ ary= Z.select{ |x| x%2 != 0 }.collect{|x| x+10}
~ p ary[0]
~ end
~ b.report 'reject+collect' do
~ ary= Z.reject{ |x| x%2 == 0 }.collect{|x| x+10}
~ p ary[0]
~ end

Performance comparison yields:
~ user system total real
map!+compact!
~ 1.202000 0.000000 1.202000 ( 1.422000)
map+compact
~ 0.651000 0.010000 0.661000 ( 0.711000)
inject
~ 5.378000 0.000000 5.378000 ( 5.608000)
external
~ 0.781000 0.000000 0.781000 ( 0.781000)
external for
~ 0.741000 0.000000 0.741000 ( 0.741000)
select+collect
~ 0.871000 0.000000 0.871000 ( 0.872000)
reject+collect
~ 0.831000 0.000000 0.831000 ( 0.831000)

for a million elements. Ranking as follows (from this one test):
map+compact
external for
external
reject+collect
select+collect
map!+compact!
inject

I take the following lessons home:
- - map/collect is fast, since it preallocates arrays.
- - external for is fast, no allocating of blocks
- - inject is evil because of its implementation

ruby 1.8.1 (2004-01-27) [i386-mswin32] btw.

Anyone more explanations ?

kaspar - code philosopher

- -- stolen off the net --
A rock pile ceases to be a rock pile the moment a single man
contemplates it, bearing within him the image of a cathedral.
~ -- Antoine de Saint-Exupery
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (MingW32)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFAh6ziFifl4CA0ImQRAgH2AJ90JK4UiUY5y260ml2jurvgNUtqgQCgnwzH
cKuNzETyV+zcgXAjcWnwh4Q=
=DA0A
-----END PGP SIGNATURE-----

Kaspar Schiess, Apr 22, 2004