Reputation: 2854
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
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
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
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
Reputation: 782
ans = []
b.each_with_index do |boolean, idx|
ans << a[idx] if boolean
end
ans
Upvotes: 1
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