efficient collect+compact ?

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

  1. 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
    #1
    1. Advertisements

  2. 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
    #2
    1. Advertisements

  3. Martin Pirker

    Ara.T.Howard Guest


    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
    #3
  4. Martin Pirker

    Chris Dutton Guest

    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
    #4
  5. 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
    #5
  6. -----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
    the first answer:

    (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
    #6
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.