Receiving array naturally?

T

Terry Michaels

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...?
 
J

Jesús Gabriel y Galán

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,

=A0process_data(string1, string2, string3, ...)

would be the same as

=A0string_array =3D [string1, string2, string3, ...]
=A0process_data(string_array)

I thought something like so would work:

=A0def process_data(*strings)
=A0 =A0 ...
=A0end

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 =3D %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.
 
R

Robert Klemme

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,

=A0process_data(string1, string2, string3, ...)

would be the same as

=A0string_array =3D [string1, string2, string3, ...]
=A0process_data(string_array)

I thought something like so would work:

=A0def process_data(*strings)
=A0 =A0 ...
=A0end

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 =3D=3D 1 && Enumerable =3D=3D=3D a.first
a.first
else
a
end.each do |x|
x.whatever
end
end

Kind regards

robert

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

Ammar Ali

You could do

def simple(*a)
=C2=A0a.flatten.each do |x|
=C2=A0 =C2=A0x.whatever
=C2=A0end
end

It's also possible to use * when passing the array to the method,
without changing the method:
some_items =3D [1, "more", %w{four six eight}]
some_method *some_items

Regards,
Ammar
 
R

Robert Klemme

You could do

def simple(*a)
=A0a.flatten.each do |x|
=A0 =A0x.whatever
=A0end
end

It's also possible to use * when passing the array to the method,
without changing the method:
some_items =3D [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


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

Terry Michaels

Clifford said:
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
 
B

botp

=A0def process_data(*strings)
=A0 =A0 ...
=A0end
yes

Do I need to write code that checks whether each object passed in is an a=
rray 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 =3D a.first if a.first.is_a? Array
a
end =3D> nil
m 1,2 =3D> [1, 2]
m [1,2] =3D> [1, 2]
m [1,2,"a",[3,4]] =3D> [1, 2, "a", [3, 4]]
m 1,2,"a",[3,4] =3D> [1, 2, "a", [3, 4]]

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

Clifford Heath

Terry said:
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
 
C

Clifford Heath

Terry said:
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 Heath.
 
T

Terry Michaels

Clifford said:
Terry said:
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 Heath.

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...
 
C

Colin Bartlett

[Note: parts of this message were removed to make it a legal post.]

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

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! :)
 
T

Terry Michaels

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

Clifford Heath.

That's pretty cool. Thanks for the good explanation.
 
D

David A. Black

Hi --

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? :)
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

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:
=> [[1, 2, 3]]

and 1.9:
=> [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
 
C

Clifford Heath

Terry said:
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 Heath.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,145
Messages
2,570,826
Members
47,371
Latest member
Brkaa

Latest Threads

Top