Receiving array naturally?

As I learn Ruby, I find a lot of flexibility in the syntax. I was
thinking there was a way to do something like so: Say I had a method
that would receive an array. Is it possible to define the method so that
it can take the array elements /either/ as an Array object, /or/ as a
comma-separated (variable) list of elements (without the brackets)?

Like,

process_data(string1, string2, string3, …)

would be the same as

string_array = [string1, string2, string3, …]
process_data(string_array)

I thought something like so would work:

def process_data(*strings)

end

That works with the comma-separated list, because the list gets combined
into one array (“strings”), but if I pass in an array object, that array
object is not split up, but rather becomes one (Array object) element of
the “strings” array.

Do I need to write code that checks whether each object passed in is an
array or not, manually splitting if necessary (which perhaps is
complicated) or is there something else I’m overlooking? Or perhaps is
my whole idea dumb from the start…?

On Wed, Sep 8, 2010 at 7:10 AM, Terry M. [email protected]
wrote:

would be the same as
That works with the comma-separated list, because the list gets combined
into one array (“strings”), but if I pass in an array object, that array
object is not split up, but rather becomes one (Array object) element of
the “strings” array.

Do I need to write code that checks whether each object passed in is an
array or not, manually splitting if necessary (which perhaps is
complicated) or is there something else I’m overlooking? Or perhaps is
my whole idea dumb from the start…?

If in the caller you know whether it’s an array or not you can use the
splat when calling the method:

array = %w{a b c d e}
process_data(*array)

keeping the definition as
def process_data(*strings)

Don’t know if this is good enough for your use case or not.

Jesus.

On Wed, Sep 8, 2010 at 7:10 AM, Terry M. [email protected]
wrote:

would be the same as
That works with the comma-separated list, because the list gets combined
into one array (“strings”), but if I pass in an array object, that array
object is not split up, but rather becomes one (Array object) element of
the “strings” array.

Do I need to write code that checks whether each object passed in is an
array or not, manually splitting if necessary (which perhaps is
complicated) or is there something else I’m overlooking? Or perhaps is
my whole idea dumb from the start…?

I wouldn’t judge on this one although I generally believe that
reduction of alternatives increases clarity.

You could do

def simple(*a)
a.flatten.each do |x|
x.whatever
end
end

def complex(*a)
if a.length == 1 && Enumerable === a.first
a.first
else
a
end.each do |x|
x.whatever
end
end

Kind regards

robert

On Wed, Sep 8, 2010 at 11:36 AM, Ammar A. [email protected]
wrote:

It’s also possible to use * when passing the array to the method,
without changing the method:

some_items = [1, “more”, %w{four six eight}]
some_method *some_items

Of course, but that’s not what Terry asked for. He wanted to make it
convenient for the caller so he could do

m(1,2,3,4)
m([1,2,3,4])
m(some_array)

Kind regards

robert

On Wed, Sep 8, 2010 at 10:09 AM, Robert K.
[email protected] wrote:

You could do

def simple(*a)
 a.flatten.each do |x|
  x.whatever
 end
end

It’s also possible to use * when passing the array to the method,
without changing the method:

some_items = [1, “more”, %w{four six eight}]
some_method *some_items

Regards,
Ammar

Terry M. wrote:

def process_data(*strings)

end

That works with the comma-separated list, because the list gets combined
into one array (“strings”), but if I pass in an array object, that array
object is not split up, but rather becomes one (Array object) element of
the “strings” array.

is there something else I’m overlooking?

You’ve overlooked the simplest way:

def process_data(*strings)
Array(strings).each do |s|

end
end

Terry M. wrote:

Clifford H. wrote:

You’ve overlooked the simplest way:
That doesn’t work, because an array passed it gets treated like simply
an element of the “strings” array:

Ah, right you are. I was thinking of this:

Array([2]) => [2]
Array(2) => [2]
but Array(*[1,2,3]) => ArgumentError: wrong number of arguments (3 for
1)

… and yes, I left out the *.
What I was thinking of was Array[*strings]

However (I knew I’d done something like this before!):

def process_data(*strings)
strings = *strings
strings.each do |s|
print s.class.to_s + " " + s.to_s + " " + “\n”
end
end

process_data(“a”, “b”, “c”)
process_data([“a”, “b”, “c”])

Does what you want.

Clifford H…

Clifford H. wrote:

Terry M. wrote:

def process_data(*strings)

end

That works with the comma-separated list, because the list gets combined
into one array (“strings”), but if I pass in an array object, that array
object is not split up, but rather becomes one (Array object) element of
the “strings” array.

is there something else I’m overlooking?

You’ve overlooked the simplest way:

def process_data(*strings)
Array(strings).each do |s|

end
end

That doesn’t work, because an array passed it gets treated like simply
an element of the “strings” array:

def process_data(*strings)
Array(strings).each do |s|
print s.class.to_s + " " + s.to_s + " " + “\n”
end
end

process_data(“a”, “b”, “c”)
process_data([“a”, “b”, “c”])

Produces:

String a

String b

String c

Array abc

Clifford H. wrote:

Terry M. wrote:

Clifford H. wrote:

You’ve overlooked the simplest way:
That doesn’t work, because an array passed it gets treated like simply
an element of the “strings” array:

Ah, right you are. I was thinking of this:

Array([2]) => [2]
Array(2) => [2]
but Array(*[1,2,3]) => ArgumentError: wrong number of arguments (3 for
1)

… and yes, I left out the *.
What I was thinking of was Array[*strings]

However (I knew I’d done something like this before!):

def process_data(*strings)
strings = *strings
strings.each do |s|
print s.class.to_s + " " + s.to_s + " " + “\n”
end
end

process_data(“a”, “b”, “c”)
process_data([“a”, “b”, “c”])

Does what you want.

Clifford H…

This seems to work.

If I might ask, what exactly is the line

strings = *strings

doing?

For that matter, I could use a little more clarification on what the ‘*’
(glob?) operator does…

Terry M. wrote:

If I might ask, what exactly is the line
strings = strings
doing?
For that matter, I could use a little more clarification on what the '

(glob?) operator does…

It’s not really a normal operator, rather it’s part of Ruby’s syntax
that happens to look like an operator, and in some cases operates like
one. It’s often called the splat operator because it looks like a
squashed bug ;-). It means different things in different contexts.

Preceding the last parameter in a method’s parameter list (excluding &,
which is also special), it says that any parameters passed in addition
to any previously declared, should be passed as an array to this
parameter.
This is how Ruby handles methods with a variable number of parameters.

In an assignment statement, it says “expand this array and do a multi
assignment”. If the value is not an array, it continues to act like a
single assignment… and that’s the magic I’m using in my example.

On the left-hand-side, this multi-assignment has only one receiver,
which
again is a special case, like the reverse of splat. It joins all the
values being assigned into an array and assigns that value. So,

strings = *strings

is a way to array-ise something that might or might not be an array.

Just a final note on multi-assignment, if there is a comma-separated
list on the left, possibly ending with a comma, only the respective
values are assigned and the rest are discarded. So these are equivalent:

a, = *strings
a = strings[0]

and so are these:

a, b = *strings
a, b = strings[0], strings[1]

The final thing you might want to do is:

a, b, *c = *strings

Umm, try it, you’ll figure out what it’s doing. Like calling a splat
method.

This is just a magic part of Ruby’s syntax, mostly useful for confusing
newbies and making your code unreadable except by wizards ;-).

Clifford H…

Clifford H. wrote:

This is just a magic part of Ruby’s syntax, mostly useful for confusing
newbies and making your code unreadable except by wizards ;-).

Clifford H…

That’s pretty cool. Thanks for the good explanation.

On Wed, Sep 8, 2010 at 1:10 PM, Terry M. [email protected]
wrote:

def process_data(*strings)

end

yes

Do I need to write code that checks whether each object passed in is an array or not

yes

, manually splitting if necessary (which perhaps is
complicated) or is there something else I’m overlooking? Or perhaps is
my whole idea dumb from the start…?

no

starting fr your splat technique…
you just need to adjust a little bit…

eg,

def m *a
a = a.first if a.first.is_a? Array
a
end
=> nil

m 1,2
=> [1, 2]

m [1,2]
=> [1, 2]

m [1,2,“a”,[3,4]]
=> [1, 2, “a”, [3, 4]]

m 1,2,“a”,[3,4]
=> [1, 2, “a”, [3, 4]]

welcome to the wonderful world of ruby.
best regards -botp

On Fri, Sep 10, 2010 at 7:10 AM, Clifford H. [email protected]
wrote:

that happens to look like an operator, and in some cases operates like
one. It’s often called the splat operator because it looks like a
squashed bug ;-). It means different things in different contexts. …

That’s a useful summary of assignment, which might well be worth putting
somewhere permanent.

This is just a magic part of Ruby’s syntax, mostly useful for confusing
newbies and making your code unreadable except by wizards ;-).

Unless you put a small comment explaining the code, but that defeats
the
object of the last part of that sentence! :slight_smile:

Hi –

On Fri, 10 Sep 2010, Clifford H. wrote:

Terry M. wrote:

If I might ask, what exactly is the line
strings = strings
doing?
For that matter, I could use a little more clarification on what the '

(glob?) operator does…

It’s not really a normal operator, rather it’s part of Ruby’s syntax
that happens to look like an operator, and in some cases operates like
one.

Wait… can you operate but not be an operator? :slight_smile:

a, b = strings[0], strings[1]

The final thing you might want to do is:

a, b, *c = *strings

You don’t need the * on strings in any of those, though:

strings = %w{ one two three }
=> [“one”, “two”, “three”]

a, = *strings; a
=> “one”

a, = strings; a
=> “one”

a,b = *strings; [a,b]
=> [“one”, “two”]

a,b = strings; [a,b]
=> [“one”, “two”]

a,b,*c = *strings; [a,b,c]
=> [“one”, “two”, [“three”]]

a,b,*c = strings; [a,b,c]
=> [“one”, “two”, [“three”]]

Those all work the same in 1.8. and 1.9, though 1.8 and 1.9 do some
parallel assignment things somewhat differently from each other. Here’s
1.8:

*a = [1,2,3]; a
=> [[1, 2, 3]]

and 1.9:

*a = [1,2,3]; a
=> [1, 2, 3]

David


David A. Black, Senior Developer, Cyrus Innovation Inc.

The Ruby training with Black/Brown/McAnally
Compleat Philadelphia, PA, October 1-2, 2010
Rubyist http://www.compleatrubyist.com

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