CHK
CHK

Reputation: 532

why does ruby parallel assignment with array of strings returns string

I'm not sure what exactly is going on with the code snippet below.

>> a, b = ["ho", "hey"]
=> ["ho", "hey"]
>> a
=> "ho"
>> b
=> "hey"    
>> c, d = "foo", "bar"
=> ["foo", "bar"]
>> c 
=> "foo"
>> d
=> "bar"
>> a, b = ["blerg"], ["baz"]
=> [["blerg"], ["baz"]]
>> a
=> ["blerg"]
>> b 
=> ["baz"]

Why wouldn't line 1 return a => ["ho"]?

So behind the scenes, what's the difference between these three assignments (a, b = ["ho", "hey"], c, d = "foo", "bar", a, b = ["blerg"], ["baz"])?

Upvotes: 7

Views: 7473

Answers (4)

Linus
Linus

Reputation: 2819

a, b = ["ho", "hey"] 

a is assigned the first element of the array, which is the string "ho". Nothing weird.

a, b = ["blerg"], ["baz"]

a, b = [["blerg"], ["baz"]]

These two are the same, as you can see by their return values. So a is assigned the first element, which is an array with one element: ["blerg"].

Similarly,

c, d = "foo", "bar"

Is the same as

c, d = ["foo", "bar"]

Upvotes: 11

Amit Kumar Gupta
Amit Kumar Gupta

Reputation: 18567

In Ruby, = takes a list of variables on the left, and a list of expressions on the right. It assigns the the first variable to the value of the first expression, the second variable the value of the second expression, and so on. If there are more variables than expressions, the leftover variables get assigned the value nil.

> a, b, c, d = "foo", 2+3, Array.new(2, 3), [:c, :d]
> a     # => "foo"
> b     # => 5
> c     # => [3, 3]
> d     # => [:c, :d]
> e     # => nil

There are two exceptions:

Left side has only one variable, right side has multiple expressions

In this case, the above rule would say that the variable just gets set to the value of the first expression on the right. Instead, the variable gets set to the Array consisting of the values of the expression on the right. In the example below, we see a gets the value [:b, :c], instead of just :b:

> a = :b, :c
> a    # => [:b, :c]

This is equivalent to:

> *a = :b , :c
> a    # => [:b, :c]

The other exception:

The left side has multiple variables, the right side has only one expression and it's an Array

Again, the original rule would imply that the first variable gets set to that whole Array, and the rest of the variables would be nil. However, that Array effectively ends up getting replaced with the list of its elements, and then = reverts to the "default" behaviour described in the beginning:

> a, b, c = [:d, [:e, :f]]
> a    # => :d
> b    # => [:e, :f]
> c    # => nil

This is equivalent to a, b, c = *[:d, [:e, :f]], or just a, b, c = :d, [:e, :f]

Upvotes: 7

Franco Rondini
Franco Rondini

Reputation: 11001

The assignment operator in the Ruby Language allow Multiple assignments (aka: parallel assignment).

Say lvalue the left side variables or attributes on the left side of the assignment operator (=) and say rvalue the right side values.

By Multiple assignments you can assign one comma delimited list to another, with corresponding variables on the left side getting corresponding values from the right

first parallel assignment

a, b = ["ho", "hey"]

if the last rvalue is an array, you can prefix it with an asterisk, which effectively expands it into its constituent values in place. the asterisk is not necessary if the rvalue is the only thing on the right-hand side the array will be expanded automatically.

To better understand, try this:

>> a,b,c =  "1",*["ho", "hey"]
=> ["1", "ho", "hey"]
>> a
=> "1"
>> b
=> "ho"
>> c
=> "hey"

let's see again:

>> a,b,c =  "1",["ho", "hey"]
=> ["1", ["ho", "hey"]]
>> a
=> "1"
>> b
=> ["ho", "hey"]
>> c
=> nil

as you can see, if we don't prefix the array with the * then the array is not expanded.

second parallel assignment

c, d = "foo", "bar"

"foo", "bar" are two literal Strings assigned to the corresponding variable on the left side.

third parallel assignment

a, b = ["blerg"], ["baz"])?

["blerg"] and ["baz"] are two Array each one containing one element of class String;

therefore each "Array of a sigle String" is assigned to the corresponding variable on the left side.

Upvotes: 0

Chris Cherry
Chris Cherry

Reputation: 28554

In the examples you point out, there are 2 different structures being used on the right hand side of the multiple assignment:

Multiple assignment from a simple array

array = ["item1", "item2"]
a, b = array
# a => "item1"
# b => "item2"

Multiple assignment from an array who's elements are themselves single element arrays (aka: a multi-dimensional array)

array = [["item1"], ["item2"]]
a, b = array
# a => ["item1"]
# b => ["item2"]

Multiple assignment unwraps only the first level of a multi-dimensional array and allows you to assign those elements to multiple variables on the left hand side of the expression. So when you do this with a multi-dimensional array, it takes the first element wich is ["item1"] and assigns in to a, then moves on to the next element, which is ["item2"] and assigns it to b. The multiple assignment doesn't "dig deeper" into the multi-dimensional array, it performs the assignment from the first level of elements.

Upvotes: 0

Related Questions