2009/10/26 Michael Randall <randallsata / gmail.com>:
> I am sure I'm making a newbie mistake, as I've just started learning
> Ruby, and would really appreciate someone pointing out what I've done
> wrong, why it is wrong, and how it *should* be done. Thanks in advance.
>
> I am writing a method definition to double any number, or all numbers if
> an array of numbers is passed to it. When I use array.collect outside of
> the method, it works as expected. When I place it inside of the method,
> instead of multiplying each integer, it treats the entire array as one
> object and double it, [1, 2, 3, 4] becoming 12341234.
>
> I've attached my test code, "simplea.rb", and below is the output when I
> run it. In case it matters to anyone, I'm running Ruby 1.8.7 under
> Cygwin (because I hate windows and don't have a Mac yet). ;)
>
> $ simplea.rb
>
> Test double([1, 2, 3, 4])
>
> 12341234
>
>
> Test double( 3 )
>
> 6
>
>
>
> Let's prove it.
> 2
> 4
> 6
> 8
>
> Attachments:
> http://www.ruby-forum.com/attachment/4184/simplea.rb

There are a few issues with the first line:

  my_a = [ n ].to_a unless n.class == "Array"

First, the test will always fail because the class and the name of the
class are of different types:

irb(main):002:0> Array == "Array"
=> false

Then, you only need either "[n]" or "n.to_a" but not both.  I
recommend the former (the latter is deprecated).

Here is _one_ way to do it:

def double(x)
  Enumerable === x ? x.map {|i| i * 2} : x * 2
end

irb(main):006:0> double 1
=> 2
irb(main):007:0> double [1,2,3]
=> [2, 4, 6]

(Note, #map is the same as #collect.)


--- spoiler warning ---


An alternative is to use the splat operator and always work with Arrays:

def double(*a)
  a.map {|i| i * 2}
end

irb(main):011:0> double 1
=> [2]
irb(main):012:0> double 1,2,3
=> [2, 4, 6]
irb(main):013:0> double [1,2,3]
=> [[1, 2, 3, 1, 2, 3]]

Or, a bit more sophisticated

def double(*a)
  a.flatten.map {|i| i * 2}
end

irb(main):017:0> double 1
=> [2]
irb(main):018:0> double 1,2,3
=> [2, 4, 6]
irb(main):019:0> double [1,2,3]
=> [2, 4, 6]

And finally with special treatment of case 1 element:

def double(*a)
  a.flatten!

  case a.size
  when 0
    raise ArgumentError, "need values"
  when 1
    a.first * 2
  else
    a.map {|i| i * 2}
  end
end

irb(main):054:0> double 1
=> 2
irb(main):055:0> double 1,2,3
=> [2, 4, 6]
irb(main):056:0> double [1,2,3]
=> [2, 4, 6]

Kind regards

robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/