Understanding Ruby Splat in Ranges and Arrays

Understanding ruby splat in ranges and arrays

I believe the problem is that splat can only be used as an lvalue, that is it has to be received by something.

So your example of *(1..9).map fails because there is no recipient to the splat, but the [*1..9].map works because the array that you are creating is the recipient of the splat.

UPDATE:
Some more information on this thread (especially the last comment): Where is it legal to use ruby splat operator?

What does * mean at the beginning of a range?

As it is stated in the documentation:

You can turn an Array into an argument list with * (or splat) operator:

arguments = [1, 2, 3]
my_method(*arguments)

The same might be done for Range:

arguments = 1..3
my_method(*arguments) # essentially the same as my_method(1, 2, 3)

Also splat operator is allowed before ranges inside the array declaration to implicitly convert Range to Array:

[*1..3]
#⇒ [1, 2, 3]

How is the splat operator understood when applied to a range expression?

The splat * converts the object to an list of values (usually an argument list) by calling its to_a method, so *1..4 is equivalent to:

1, 2, 3, 4

On its own, the above isn't valid. But wrapped within square brackets, [*1..4] becomes:

[1, 2, 3, 4]

Which is valid.

You could also write a = *1..4 which is equivalent to:

a = 1, 2, 3, 4
#=> [1, 2, 3, 4]

Here, the list of values becomes an array due to Ruby's implicit array assignment.

Where is it legal to use ruby splat operator?

First, precedence isn't an issue here, because foo = bar || (*zap) works no better. The general rule of thumb is that you cannot perform additional operations on a splat. Even something as simple as foo = (*zap) is invalid. This applies to 1.9 as well.

Having said that, what do you expect foo = bar || *zap to do, if it worked, that is different than foo = bar || zap? Even in a case like a, b = bar || *zap (which also doesn't work), a, b = bar || zap accomplishes what I'd assume would be the same thing.

The only situation where this might make any sense is something like a, b = foo, bar || *zap. You should find that most cases where you would want to use this are covered by a, b = foo, *(bar || zap). If that doesn't cover your case, you should probably ask yourself what you really hope to accomplish by writing such an ugly construct.


EDIT:

In response to your comments, *zap || bar is equivalent to *(zap || bar). This demonstrates how low the splat's precedence is. Exactly how low is it? The best answer I can give you is "pretty low".

For an interesting example, though, consider a method foo which takes three arguments:

def foo(a, b, c)
#important stuff happens here!
end

foo(*bar = [1, 2, 3]) will splat after the assignment and set the arguments to 1, 2, and 3 respectively. Compare that with foo((*bar = [1, 2, 3])) which will complain about having the wrong number of arguments (1 for 3).

Confusion with splat operator and Range in Ruby

I think of parallel assignment as setting an array of variables equal to another array with pattern matching.

One point is that a range is a single value until you convert it to an array or splat it. For instance [1..5] which is a one element array of the range 1..5 and not [1,2,3,4,5]. To get the array of ints you need to do (1..5).to_a or [*(1..5)]


The first one i think is the trickiest. If the splatted var is assigned to one element, the var itself must be a one-element array:

*a = 5
a
# => [ 5 ]

For the next two, splat takes 0 or more not already assigned values into an array. So the following makes sense:

*a, b = (1..8)

is like

*a, b = "hey"

which is like

*a, b = [ "hey" ]

so *a is [] and b is "hey" and by the same logic that if *a is nothing, a must be an empty array. Same idea for

a, *b = (1..5)

For the next one, the range is splatted, so the assignment makes a lot of sense again:

[*(2..4), 9, 5]
# => [2, 3, 4, 9, 5]

And parallel assignment with a splat again. Next one is similar:

[*3, *4, *5]
# => [3, 4, 5]

So that's like

a = 3, 4, 5

which is like

a = [3, 4, 5]

Ruby * operator before array

* is the splat operator. It is used to split an array into a list of arguments.

line.split(/=|;/) returns an array. To create a Hash, each element of the array must be passed as an individual parameter.

Correct way to populate an Array with a Range in Ruby

You can create an array with a range using splat,

>> a=*(1..10)
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

using Kernel Array method,

Array (1..10)
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

or using to_a

(1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Why was I able to get away with not using a splat operator sometimes

Well, let's check it out. There is no any magic behind attributes.extract! in the end. Here is an actual implementation of this method from Rails source code:

def extract!(*keys)
keys.each_with_object(self.class.new) { |key, result|
result[key] = delete(key) if has_key?(key)
}
end

Link: click. As you can see, it creates new hash, goes over the keys one by one and moves value from self to this new array. So, if you give an Array argument to this method then key in the block will be an Array as well. So, it won't be found. So, no way it may work for array argument. The only one possibility is that something else is passed instead of User::HOME_ADDRESS_FIELDS.

Create new arrays from nested arrays based on corresponding indexes

You are simply transposing the inner arrays:

arrays.map(&:transpose)
#=> [
# [
# ["ab", "ik", "rs"],
# ["cd", "lm", "tu"],
# ["ef", "no", "vw"],
# ["gh", "pq", "xy"]
# ],
# [
# ["z1", "89", "GH"],
# ["23", "AB", "IJ"],
# ["45", "CD", "KL"],
# ["67", "EF", "MN"]
# ]
# ]


Related Topics



Leave a reply



Submit