1

Is there a way to refactor this method using map? (this intention of this method is to return an array of prime numbers between 1 and the max parameter)

def primes(max)
  prime_arr = []
   (1..max).each {|i| prime_arr << i if is_prime?(i)}
  prime_arr
end

is_prime?(val) returns true or false.

if i change my method to:

def primes(max)
 (1..max).map {|i| i if is_prime?(i)}
end

The returned array has a nil value when the code blocks fails.

p primes(5)
#=> [1, 2, 3, nil, 5]

I know what I have works, but i'd rather not declare the array and return the array specifically if there's a better way to do it

Brent
  • 114
  • 1
  • 7

3 Answers3

2

Enumerable.reject (or Enumerable.select) are the correct methods to filter the sequence.

These filter methods only looks at the truthy-ness of the returned value and thus while returning i in this case "works" (as i is always a truthy value), the returned value is actually discarded (unlike map) so the predicate should look more like:

c.reject {|i| !is_prime?(i)}

# or (inverting the double negative)
c.select {|i| is_prime?(i)}

# or (with &:)
c.select(&:is_prime?)

(See &: for the magic behind the latter case.)

Also, since this is dealing with finding prime numbers, the Sieve of Eratosthenes might be a good read.

Community
  • 1
  • 1
user2864740
  • 60,010
  • 15
  • 145
  • 220
  • Yeah, i realized that seconds after posting the questions :) reject and select is what i was looking for. – Brent Oct 31 '13 at 20:58
  • this was more a question about how to avoid the nils on a map, inject, etc. (i'm new to ruby), I suppose the most correct way is to include Primes and Primes.each through the max. – Brent Oct 31 '13 at 21:01
1
require 'Prime'
MAX = 100
p Prime.each(MAX).to_a      #=> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31...
p (1..MAX).select(&:prime?) #=> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31...

An alternative is to use grep if you want to select and transform:

primes = ->x{x.prime?} # (new lambda syntax)
p (1..100).grep(primes){|x| x.to_s} #=> ["2", "3", "5", "7", "11", "13", "17",...
hirolau
  • 13,451
  • 8
  • 35
  • 47
  • That grep example is interesting, but really needs to be explored more ([Proc#===](http://ruby-doc.org/core-2.0.0/Proc.html#method-i-3D-3D-3D) should be mentioned because it's the entire reason why it works; and the lambda can be substituted with the `&:` to-proc approach). I normally just use `select..map..` in a chain. – user2864740 Oct 31 '13 at 20:49
  • Grep uses the case equality operator in order to select items. You can use Regexp or Class names. Just as you mentioned a lambda (or Proc) is called when compared with ===. This comes in very handy when creating case statements or want to refactor logic from complex chain-functions. – hirolau Oct 31 '13 at 20:55
  • @hirolau - thanks for the info! I was, actually, asking for select and reject (apparently); i just didn't know it when i asked. – Brent Oct 31 '13 at 21:07
0

Yeah... i just figured out A way while trying to explain it, but please feel free to show me a better way!

def primes(max)
  (1..max).reject {|i| i if ! is_prime?(i)}
end
Brent
  • 114
  • 1
  • 7