Find first n elements from array that match a condition - ruby

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!!

arr = []
Prime.each{|p| arr << p if p > 100; break if arr.count == 10 }
puts arr

Some of the answers have the disadvantage that they enumerate primes below the threshold multiple times. Here is one way to avoid that:
require 'prime'
def check_for_primes(start_number, desired_size)
return [] if desired_size.zero?
enum = Prime.each
[enum.find { |n| n >= start_number }] + enum.first(desired_size-1)
end
check_for_primes(100, 10)
#=> [101, 103, 107, 109, 113, 127, 131, 137, 139, 149]
Alternatively, this could be written:
def check_for_primes(start_number, desired_size)
return [] if desired_size.zero?
enum = Prime.each
(0..1.0/0).each_with_object([]) do |_,arr|
n = enum.next
if n >= start_number
arr << n
return arr if arr.size == desired_size
end
end
end

A simple iterative way:
require 'prime'
initial = 100
list = []
10.times do |x|
initial = Prime.find {|p| p > initial}
list << initial
end
puts list

You can use Prime.each(n) to have an upper limit:
Prime.each(1000).drop_while { |p| p <= 100 }.take(10)
# => [101, 103, 107, 109, 113, 127, 131, 137, 139, 149]
Alternatively, you can count have many primes are below 100, then take that amount + 10:
Prime.take(Prime.take_while { |p| p <= 100 }.count + 10)[-10..-1]
# => [101, 103, 107, 109, 113, 127, 131, 137, 139, 149]

Related

the result from my custom each method is producing a nil in ruby

def custom_each(array)
index = 0
while index < array.length
yield array[index]
index += 1
end
end
ages = [12, 45, 67, 89]
p custom_each(ages) { |num| num * 2 }
Any ideas? im a total beginner?
im doing this course https://www.rubyguides.com/2018/10/ruby-map-method/
Your custom_each method works just fine. All you have to do is move p inside the block:
# built-in each
ages.each { |num| p num * 2 }
# your custom_each
custom_each(ages) { |num| p num * 2 }
Output:
24
90
134
178
Note that the original Array#each doesn't return an altered array either. It returns an enumerator or the original array:
[12, 45, 67, 89].each
#=> #<Enumerator: [12, 45, 67, 89]:each>
[12, 45, 67, 89].each { |num| num * 2 }
#=> [12, 45, 67, 89]
By default last executed expression in the function will returned. The result of the block execution yield array[index] haven't saved and returned from the function custom_each. That's why it returns nil always
Try the below:
def custom_each(array)
index = 0
result = []
while index < array.length
result << yield(array[index]) # Saves the result of block execution in an array
index += 1
end
result # Returns the result
end
ages = [12, 45, 67, 89]
p custom_each(ages) { |num| num * 2 }
Output:
[24, 90, 134, 178]

How to return "invalid" instead a sum of an array when input is a string or a float

I want to return an array of sums of multiples of 3 and 5 from 0 to n. And I want to return "invalid" when the input is a string, a float or < 0
def is_multiple_of_3_or_5(n)
if n.class == Integer && n > 0
n % 3 == 0 || n % 5 == 0 ? true : false
else
puts "invalid"
end
end
def sum_of_3_and_5_multiples(n)
if n.class == Integer
i = 0
array_of_multiples_of_3_and_5 = Array.new
while i < n
array_of_multiples_of_3_and_5 << i if is_multiple_of_3_or_5(i) == true
i += 1
end
array_of_multiples_of_3_and_5.inject(0, :+)
end
end
sum_of_3_and_5_multiples(-1)
To get the sums of multiples of 3 and 5 I got this but when I try with -1 that return me 0 instead "invalid", with"string"` that return me an error.
You havent' put any code in your sum_of_3_and_5_multiples method to handle what happens if is_multiple_of_3_or_5 is invalid (or to put it another way, a string). You also don't need to puts 'invalid', as this returns a value of null. Just 'invalid' will do:
def is_multiple_of_3_or_5(n)
if n.class == Integer && n > 0
n % 3 == 0 || n % 5 == 0 ? true : false
else
"invalid"
end
end
def sum_of_3_and_5_multiples(n)
if n.class == Integer
i = 0
array_of_multiples_of_3_and_5 = Array.new
while i < n
return "invalid" if is_multiple_of_3_or_5(i).is_a?(String)
array_of_multiples_of_3_and_5 << i if is_multiple_of_3_or_5(i) == true
i += 1
end
array_of_multiples_of_3_and_5.inject(0, :+)
end
end
sum_of_3_and_5_multiples(-1)
=> "invalid"
One could do that as follows.
def sum_of_3_and_5_multiples(n)
case n
when Float, String, -Float::INFINITY...0
return 'invalid'
end
((0..n).step(3).to_a + (0..n).step(5).to_a).uniq
end
sum_of_3_and_5_multiples(11.5)
#=> "invalid"
sum_of_3_and_5_multiples("11")
#=> "invalid"
sum_of_3_and_5_multiples(-340)
#=> "invalid"
sum_of_3_and_5_multiples(15)
#=> [0, 3, 6, 9, 12, 15, 5, 10]
sum_of_3_and_5_multiples(87)
#=> [0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45,
# 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87,
# 5, 10, 20, 25, 35, 40, 50, 55, 65, 70, 80, 85]
Alternative verbose option, using a monkey patch to String class and a custom Class, stealing from Cary's answer (https://stackoverflow.com/a/59876202/5239030) but with a three dot Range for excluding the extreme value.
Patching the String class for using methods like this Numeric#integer? and Numeric#positive?. I'd suggest to use Kernel#raise in case of error.
module MyStringPatch
def integer?
false
end
def positive?
false
end
end
String.include MyStringPatch
Writing the custom class
class MyNumber
def initialize(n)
raise 'Invalid' unless n.integer? && n.positive?
#n = n
end
def sum_of_3_and_5_multiples
(((0...#n).step(3).to_a + (0...#n).step(5).to_a).uniq).sum
end
end
Finally using it
n = 32
my_number = MyNumber.new(n)
p my_number.sum_of_3_and_5_multiples
#=> 225
Or ...in initialize': Invalid (RuntimeError) in case of n = "32" or n = -32 or n = 32.0.
You can use something like:
return "invalid" unless n.is_a? Integer || n.positive?
Taking a look at: https://rubystyle.guide/ may help
I've found this ! that worked !
def is_multiple_of_3_or_5(n)
n % 3 == 0 || n % 5 == 0 || n == 0 ? true : false
end
def sum_of_3_and_5_multiples(n)
puts n.class
if n.class == Integer && n >= 0
i = 0
array_of_multiples_of_3_and_5 = Array.new
while i < n
array_of_multiples_of_3_and_5 << i if is_multiple_of_3_or_5(i) == true
i += 1
end
return array_of_multiples_of_3_and_5.inject(0, :+)
end
if n.class != Integer || n < 0
return "invalid"
end
end
thanks for your help that was helpful !

Index Error while transposing a 5x5 array

Given the following "bingo board" array:
board = [[47, 44, "X", 8, 88],
[22, 69, "X", 65, 73],
[83, 85, "X", 89, 57],
[25, 31, "X", 68, 51],
[75, 70, "X", 80, 83]]
and running the following method to see if I have a bingo:
def bingo(board)
if board.each_index {|e| board[e].uniq!} .any? {|row| row == ["X"]} == true
p "BINGO!"
elsif
board = board.transpose
board.each_index {|e| board[e].uniq!} .any? {|row| row == ["X"]} == true
p "BINGO!"
elsif (board[0][0] && board[1][1] && board[2][2] && board[3][3] && board[4][4]) == "X"
p "BINGO!"
elsif board[0][4] && board[1][3] && board[2][2] && board[3][1] && board[4][0] == "X"
p "BINGO!"
else
p "no bingo"
end
end
I'm running into the following error:
`transpose': element size differs (5 should be 4) (IndexError)
What gives? Why does it think 5 be 4?
I believe your second line, running board[e].uniq! changes each of the arrays nested within the original board array destructively... hence, there are no longer five objects in each line.

How can I convert this array so that each element represents the cumulative value of the previous elements? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Cumulative array sum in Ruby
I have an array of integers like this
[20, 25, 40, 60]
How can I turn it into an array with each element representing the cumulative value of the elements before it, including itself?
[20, 45, 85, 145]
I'm using Rails 3.2.0 & ruby 1.9.3
s = 0
[20, 25, 40, 60].map{|e| s += e}
[20, 25, 40, 60].reduce([]) do |arr, v|
arr << (arr.last || 0) + v
end
Or an ugly one liner.
[20, 25, 40, 60].reduce([0]){ |a, v| a << a[-1] + v }[1..-1]
array = [20, 25, 40, 60]
(array.size - 1).times { |i| array[i + 1] += array[i] }
puts array
# => [20, 45, 85, 145]
arr = [20, 25, 40, 60]
first = []
sum = 0
arr.each do |e|
sum += e
first << sum
end
puts first
arr.each_with_index.map{|x, i| x + (i==0 ? 0 : arr[0..i-1].inject(:+))}
=> [20, 45, 85, 145]
Matlab:
B = cumsum(A)
Ruby:
class Array
def ruby_cumsum!
(1..size-1).each {|i| self[i] += self[i-1] }
self
end
end

Is there a shortcut to setting a variable to the greater of 2 values?

Say I'm looking for the greater of 2 sets of n numbers (for the sake of example) and I had this algorithm:
def maxofarrays set1 set2
greater_array = []
set1.each_index do |index|
if set1[index] > set2[index] then greater_array << set1[index]
else greater_array << set2[index]
end
greater_array
end
Is there a shortcut to the two innermost lines of code? Or do I have to type it out?
a = [347, 163, 436, 234, 113]
b = [213, 566, 124, 212, 963]
c = a.zip(b).map(&:max)
#=> [347, 566, 436, 234, 963]

Resources