Ruby * Operator Before Array

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.

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]

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?

Using [] with the Safe Navigation Operator in Ruby

Just use the ordinary (non-sugar) form.

request.path.match(/\A\/(?<slug>(?!admin|assets)\w+)/)&.[](:slug)

Clarification on the Ruby Operator

Ruby is an object-oriented language. The fundamental principle of object orientation is that objects send messages to other objects, and the receiver of the message can respond to the message in whatever way it sees fit. So,

a << b

means whatever a decides it should mean. It's impossible to say what << means without knowing what a is.

As a general convention, << in Ruby means "append", i.e. it appends its argument to its receiver and then returns the receiver. So, for Array it appends the argument to the array, for String it performs string concatenation, for Set it adds the argument to the set, for IO it writes to the file descriptor, and so on.

As a special case, for Fixnum and Bignum, it performs a bitwise left-shift of the twos-complement representation of the Integer. This is mainly because that's what it does in C, and Ruby is influenced by C.

ruby's = operator and sort method

Before you can understand sorting objects. You need to understand the .sort method in Ruby. If you were to sort 5 cards with numbers on them, you could take a look at all of them, find the lowest one easily, and just pick that one as your first card (presuming you're sorting from lowest to highest, which Ruby does). When your brain sorts, it can look at everything and sort from there.

There are 2 main elements of confusion here that are rarely addressed:

1) Ruby can't sort in the way you think of the word "sort". Ruby can only 'swap' array elements, and it can 'compare' array elements.

2) Ruby uses a comparison operator, called the spaceship, to attribute numbers to help it 'sort'. Those numbers are -1,0,1. People erroneously think those 3 numbers are helping it 'sort' (eg. if there was an array with 3 numbers such a 10,20,30, then the 10 would be a -1, the 20 a 0, and the 30 a 1, and Ruby is just simplifying the sorting by reducing it to -1,0,1. This is wrong. Ruby can't "sort". It can't only compare).

Look at the spaceship operator. It's 3 individual operators lumped into one, the <, the =, and the >. When Ruby compares two variables, it results in one of these numbers.

Spaceship Operator

That said, what does "results" mean? It DOESN'T mean that one of the variables is assigned a 0,1,-1. It simply is a way what Ruby can take two variables and do something with them. Now, if you just run:

puts 4 <=> 5

You'll get the result of -1, since whatever 'part' (eg. <, =, or >) of the comparison operator (spaceship) is true, gets the number that's assigned to it (as seen in the above picture). When Ruby sees this <=> with an array though, it has 2 things it will do to the array only: Leave the array alone OR swap the elements of the array.

If Ruby uses the <=> and gets a 1, it will swap the 2 elements of the array. If Ruby gets a result of -1 or 0, it will leave the array alone.

An example is if Ruby sees the array [2,1]. The sort method would make it pull in these figures like 2<=>1. Since the part of the spaceship (if you want to think of it like that) that's true is the > (ie. 2>1 is true), the result is '1' from Ruby. When Ruby sees a 1 result from the spaceship, it swaps the 2 elements of the array. Now the array is [1,2].

Hopefully at this point, you see that Ruby only compares with the <=> operator, and then swaps (or leaves alone) the 2 elements in the array it compares.

Understand the .sort method is an iterative method, meaning that it's a method that runs a block of code many times. Most people are introduced to the .sort method only after they've seen a methods such as .each or .upto (you don't need to know what those do if you haven't heard of them), but those methods run through the array 1 time ONLY. The .sort method is different in that it will run through your array as many times as it needs to so that it's sorted (by sorted, we mean compared and swapped).

To make sure you understand the Ruby syntax:

foo = [4, 5, 6]
puts foo.sort {|a,b| a <=> b}

The block of code (surrounded by {}'s) is what Ruby would do any way when it sorts from lowest to highest. But suffice it to say that the first iteration of the .sort method will assign the variables between the pipes (a, b) the first two elements of the array. So for the first iteration a=4 and b=5, and since 4<5, that results in a -1, which Ruby takes it to mean to NOT swap the array. It does this for a second iteration, meaning a=5 and b=6, sees that 5<6, results in -1 and leaves the array alone. Since all the <=> results were -1, Ruby stops looping through and feels the array is sorted at [4,5,6].

We can sort from high to low by simply swapping the order of the variables.

bar = [5, 1, 9]
puts bar.sort {|a,b| b <=> a}

Here's what Ruby is doing:

Iteration 1: Array [5,1,9]. a=5, b=1. Ruby sees the b<=>a, and says is 1 < 5? Yes. That results in -1. Stay the same.

Iteration 2: Array [5,1,9]. a=1, b=9. Ruby sees the b<=>a, and says is 9 < 1? No. That results in 1. Swap the 2 array elements. The array is now [5,9,1]

Iteration 3: Array [5,9,1]. Starting over b/c there was a +1 result in the array before going through it all. a=5, b=9. Ruby sees the b<=>a, says is 9<5? No. That results in 1. Swap. [9, 5, 1]

Iteration 4: Array [9,5,1]. a=5, b=1. Ruby sees the b<=>a, says is 1<5? Yes. That results in -1. Therefore, no swapping is performed. Done. [9,5,1].

Imagine an array with the number 50 for the first 999 elements, and a 1 for element 1000. You fully understand the sort method if you realize Ruby has got to go through this array thousands of times doing the same simple compare and swap routine to shift that 1 all the way to the beginning of the array.

Now, we can finally look at .sort when comes to an object.

def <=>(other)
other.score <=> score
end

This should now make a little more sense. When the .sort method is called on an object, like when you ran the:

@players.sort

it pulls up the "def <=>" method with the parameter (eg. 'other') which has the current object from @players (eg. 'whatever the current instance object is of '@players', since it's the sort method, it's eventually going to go through all of the elements of the '@players' array). It's just like when you try to run the puts method on a class, it automatically calls the to_s method inside that class. Same thing for the .sort method automatically looking for the <=> method.

Looking at the code inside of the <=> method, there must be a .score instance variable (with an accessor method) or simply a .score method in that class. And the result of that .score method should (hopefully) be a String or number - the 2 things ruby can 'sort'. If it's a number, then Ruby uses it's <=> 'sort' operation to rearrange all of those objects, now that it knows what part of those objects to sort (in this case, it's the result of the .score method or instance variable).

As a final tidbit, Ruby sorts alphabetically by converting it to numerical values as well. It just considers any letter to be assigned the code from ASCII (meaning since upper case letters have lower numerical values on the ASCII code chart, upper case will be sorted by default to be first).

Hope this helps!

What is the Ruby = (spaceship) operator?

The spaceship operator will return 1, 0, or −1 depending on the value of the left argument relative to the right argument.

a <=> b :=
if a < b then return -1
if a = b then return 0
if a > b then return 1
if a and b are not comparable then return nil

It's commonly used for sorting data.

It's also known as the Three-Way Comparison Operator. Perl was likely the first language to use it. Some other languages that support it are Apache Groovy, PHP 7+, and C++20.

Is there an elegant way to iterate through an array and stop before the end?

What you are effectively doing is, first mapping the elements of the Array with a transformation, and then finding the first transformed element that satisfies some predicate.

We can express this like this (I'm re-using the definitions from @iGian's answer, but with an added side-effect so that you can observe the evaluation of the transformation operation):

def complex_stuff_with(e)
p "#{__callee__}(#{e.inspect})"
e**2
end

ary = [1, 2, 3, 2]
x_i_want = 4

ary
.map(&method(:complex_stuff_with))
.find(&x_i_want.method(:==))
# "complex_stuff_with(1)"
# "complex_stuff_with(2)"
# "complex_stuff_with(3)"
# "complex_stuff_with(2)"
#=> 4

This gives us the correct result but not the correct side-effects. You want the operation to be lazy. Well, that's easy, we just need to convert the Array to a lazy enumerator first:

ary
.lazy
.map(&method(:complex_stuff_with))
.find(&x_i_want.method(:==))
# "complex_stuff_with(1)"
# "complex_stuff_with(2)"
#=> 4

Or, using the definitions from your question:

array
.lazy
.map(&method(:complex_stuff_with))
.find(&:is_the_one_i_want?)

How does Ruby's Combined Comparison Operator work?

First question: At various points in its operation the sort method has to compare pairs of objects to see what their relative ordering should be. It does the comparison by applying the block you pass to sort, i.e., {|first, second| second <=> first}. Not sure what you mean by "how do the items within the array become objects when we don't declare them as such?". All data in ruby is an object, so there's no declaration or conversion needed given that all variables are object references.

Second question: Yes, you could do fruits.sort.reverse, but that would require additional work after the sort to do the reverse operation. Also, reverse can't handle more complex sorting tasks, such as sorting people by multiple criteria such as gender & last name, or hair color, height, and weight. Writing your own comparator can handle quite complex orderings.

Passing a comparison operator as argument to a method in Ruby

The comparison operators are just methods in Ruby, so you could do:

1 <= 2 # is the same as
1.<=(2)

which means you can public_send them just as any other public method:

1.public_send(:<=, 2)


Related Topics



Leave a reply



Submit