Array#collect in a method call, not working for me

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). :wink:

$ simplea.rb

Test double([1, 2, 3, 4])

12341234

Test double( 3 )

6

Let’s prove it.
2
4
6
8

On Mon, Oct 26, 2009 at 1:19 PM, Michael R. [email protected]
wrote:

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.

Using code similar to yours…
You could try this or something like this.

def double(n)
[n].flatten.map {|num| num * 2 }
end

puts double([1, 2, 3, 4])
puts
puts double(3)

Harry

2009/10/26 Michael R. [email protected]:

I’ve attached my test code, “simplea.rb”, and below is the output when I
Test double( 3 )

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

OK, I thought that part was working, but now I see that it wasn’t
really. So to help me understand I wrote the following, to see what it
does and I see that adding the #inspect will give me the string to
compare against.

But does that mean that (a = [1, 2].class) is setting a to be an Array?
Oh, and thank you!

Here is my test…

irb(main):034:0* def proovy( n )
irb(main):035:1> a = n.class.inspect
irb(main):036:1> b = n.class
irb(main):037:1> puts “a: #{a}”
irb(main):038:1> puts “b: #{b}”
irb(main):039:1> case a
irb(main):040:2> when “Array” : puts “a matched Array in case”
irb(main):041:2> when “Fixnum” : puts “a matched Fixnum in case”
irb(main):042:2> end
irb(main):043:1> case b
irb(main):044:2> when “Array” : puts “b matched Array in case”
irb(main):045:2> when “Fixnum” : puts “b matched Fixnum in case”
irb(main):046:2> end
irb(main):047:1> end
=> nil
irb(main):048:0> proovy(1)
a: Fixnum
b: Fixnum
a matched Fixnum in case
=> nil
irb(main):049:0> proovy([1, 2, 3])
a: Array
b: Array
a matched Array in case
=> nil

Robert K. wrote:

2009/10/26 Michael R. [email protected]:

I’ve attached my test code, “simplea.rb”, and below is the output when I
Test double( 3 )

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

[ … ]

robert

But does that mean that (a = [1, 2].class) is setting a to be an Array?

Consider this:

n = []
=> []

a = n.class
=> Array

a << 1
NoMethodError: undefined method `<<’ for Array:Class

a doesn’t become an array, it becomes the class type of Array.

Regards,

  • Mac

Michael L. wrote:

But does that mean that (a = [1, 2].class) is setting a to be an Array?
Consider this:

n = []
=> []

a = n.class
=> Array

a << 1
NoMethodError: undefined method `<<’ for Array:Class

a doesn’t become an array, it becomes the class type of Array.
Regards,

  • Mac

That makes sense. And I don’t know why I didn’t think to do the
following to test it myself…

irb(main):061:0> n = []
=> []
irb(main):062:0> a = n.class
=> Array
irb(main):063:0> a.class
=> Class

THANKS!!

Please do not top post.

2009/10/26 Michael R. [email protected]:

irb(main):034:0* def proovy( n )
irb(main):045:2> when “Fixnum” : puts “b matched Fixnum in case”
irb(main):046:2> end
irb(main):047:1> end

Note that you can simplify the test:

irb(main):004:0> [[], “foo”].each {|obj| case obj
irb(main):005:2> when Array
irb(main):006:2> printf “%p is an array\n”, obj
irb(main):007:2> when String
irb(main):008:2> printf “%p is a string\n”, obj
irb(main):009:2> else
irb(main):010:2* printf “i don’t know what %p is.\n”, obj
irb(main):011:2> end}
[] is an array
“foo” is a string
=> [[], “foo”]

case uses the === operator:

irb(main):012:0> Array === [1,2,3]
=> true
irb(main):013:0> Array === “foo”
=> false
irb(main):014:0> String === “foo”
=> true

IMHO testing against the class is superior to testing against the
type’s name. Here’s why: in case of inheritance the name test will
fail while the class test still succeeds

irb(main):016:0> class FooArray < Array; end
=> nil
irb(main):017:0> Array === FooArray.new
=> true
irb(main):018:0> “Array” === FooArray.name
=> false

Kind regards

robert

my_a = [ n ].to_a unless n.class == “Array”

I think what you were trying to write is:

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

There is actually a Ruby idiom for this:

my_a = Array(n)

Type ‘ri Kernel#Array’ for more info.

This is a normal method which just happens to start with a capital
letter, which can be confusing. The Ruby interpreter doesn’t confuse
this with a constant (or class name) because it is followed by
parentheses, so it must be a method call.

Another version of your original code would be:

my_a = n.is_a?(Array) ? n : [n]

However such class tests are very rare in idiomatic Ruby. Duck-typing
means you don’t care what class an object is, just what methods it
responds to.

Since the only method you are going to call is ‘collect’, you could
write:

my_a = n.respond_to?(:collect) ? n : [n]

Then the caller could pass in any object which has a ‘collect’ method,
and you would use that. Only if it doesn’t would it get wrapped in an
Array.

The benefit here is that you could pass in an Enumerable object (such as
an open file), and you could process its elements as they are read
without having to turn the whole file into an Array first.

But equally reasonable would be to drop the [n] part altogether, and
simply define the method as taking an Enumerable object. It would then
be the caller’s responsibility to wrap the object if required.

def double( n )
n.collect {|num| num * 2 }
end

puts “\nTest double([1, 2, 3, 4])”
inttest = double( [1, 2, 3, 4] )
puts “\n#{inttest}\n\n”

puts “\nTest double( [3] )”
inttest = double( [3] )
puts “\n#{inttest}\n\n”

This is what tends to happen in idiomatic ruby - the number of lines of
code becomes extremely small :slight_smile:

HTH,

Brian.

Brian C. wrote a bunch of helpful and educational stuff:
[ … ]

HTH,

Brian.

More than helpful Brian. Quite educational, and I applaud you.

Michael

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs