I want to select the first 10 elements of an array that match a certain condition without having to loop through the whole array. I know that find
gets me the first element. So for example, the code below gives me the first prime that is larger than 100:
require 'prime'
puts Prime.find {|p| p > 100 } #=> 101
Is there a way to get the first 10 primes that are bigger then 100?
In Ruby 2.0+ you can write:
require 'prime'
Prime.lazy.select{|p| p > 100 }.take(10).to_a #=> [101, 103, 107, 109, 113, 127, 131, 137, 139, 149]
You can do it more manually like
def check_for_primes(start_number, desired_size)
result = []
suspect = start_number
while result.size < desired_size do
result << suspect if suspect.prime?
suspect += 1
end
result
end
check_for_primes 100, 10
#=> [101, 103, 107, 109, 113, 127, 131, 137, 139, 149]
with a simple ruby iteration.
Which works with all ruby versions.
instead of the (indisputably non ruby like) while loop, we can add @cary-swoveland 's variation, which has quite some ruby goodness in it.
check_enum_text(start_number, desired_size)
(start_number..1.0/0).each_with_object([]) do |n,arr|
if n.prime?
arr << n;
return arr if arr.size == desired_size
end
end
end
***********UPDATE***********
and some benchmarks for performance
require 'benchmark'
a_gazillion = 10000000000
Benchmark.bm do |x|
x.report("lazy_select") { Prime.lazy.select{|p| p > (a_gazillion / 1000) }.take(10).to_a }
x.report("prime_each") { arr = []; Prime.each{|p| arr << p if p > a_gazillion / 1000; break if arr.count == 10 } }
x.report("while_loop") { check_for_primes a_gazillion, 10 }
x.report("enum_text") { check_enum_text a_gazillion, 10 }
end
user system total real
lazy_select 75.360000 0.240000 75.600000 (84.259781)
prime_each 6.100000 0.040000 6.140000 ( 6.730646)
while_loop 0.620000 0.000000 0.620000 ( 0.655504)
enum_text 0.610000 0.000000 0.610000 ( 0.770726)
from what we see the two latest solutions are the ones that perform the best. From some extra benchmarking (by tweaking the desired_size) I can not conclude to which one is better
def bench(start, length)
Benchmark.bm do |x|
x.report("enum_text") { check_enum_text start, length }
x.report("while_loop") { check_for_primes start, length}
end
end
bench a_gazillion, 100
user system total real
enum_text 6.350000 0.000000 6.350000 ( 6.974557)
while_loop 6.530000 0.000000 6.530000 ( 7.330884)
bench a_gazillion, 500
user system total real
enum_text 31.880000 0.110000 31.990000 ( 36.723209)
while_loop 32.850000 0.060000 32.910000 ( 38.569744)
Performance is similar (actually @cary-swoveland's solution performs slightly better), so I will have to go with that solution since it is more ruby like!!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With