Steve
Steve

Reputation: 2854

What is the best way to select array elements using an array of true/false values?

I have two arrays:

a = [1, 2, 3, 4]
b = [true, true, false, false]

What is the best way to select the elements of a whose corresponding element in b is true? In this example, I expect:

result = [1, 2]

I am doing it like this:

zipped = a.zip(b)
zipped.select{ |z| z[1] }.map{ |n| n.first }
# => [1, 2]

I would like to know of the best solution in pure Ruby, but am also open to suggestions from GSL etc.

Performance:

array_size = 4000
iterations = 1000

Benchmark.bm do |bm|
  bm.report do
    iterations.times do
      a.each_with_index.map { |a, i| a if b[i] }.compact
    end
  end
end

Benchmark.bm do |bm|
  bm.report do
    iterations.times do
      zipped = a.zip(b).select{ |z| z[1] }.map{ |n| n.first }
    end
  end
end

The performance below shows that not only is map on each_with_index more readable, but is also faster on large arrays.

     user     system      total        real
   0.610000   0.000000   0.610000 (  0.608642)
       user     system      total        real
   0.880000   0.000000   0.880000 (  0.882018)

Upvotes: 1

Views: 112

Answers (5)

Jörg W Mittag
Jörg W Mittag

Reputation: 369594

I find this quite readable and expressing the semantics clearly.

a.select.with_index {|_, i| b[i] }

As it turns out, @steenslag beat me by 4 minutes with an even more elegant approach :-D

Upvotes: 6

steenslag
steenslag

Reputation: 80085

Turn the booleans into an Enumerator - no zip and no array lookup.

a = [1,2,3,4]
b = [true, true, false, false].to_enum

a.select{b.next}  # => [1, 2]

Upvotes: 11

eugen
eugen

Reputation: 9226

If you're looking for something shorter

a.each_with_index.map { |a, i| a if b[i] }.compact will do, but it is neither faster nor more readable. You'll have to define what best means.

Upvotes: 2

alebian
alebian

Reputation: 782

ans = []
b.each_with_index do |boolean, idx|
  ans << a[idx] if boolean
end
ans

Upvotes: 1

ConnorCMcKee
ConnorCMcKee

Reputation: 1645

If your goal is simply succinctness, and you already have a, b, and a results array defined, my first instinct would be to just say:

b.each_with_index { |val,i| results.push( a[i] ) if val }

Now, is this better? Not necessarily, no... but you'll want to expound upon your definition before anyone can conclusively say so.

Upvotes: 1

Related Questions