Ruby 'Range.last' does not give the last value. Why? - ruby

When using the triple dot notation in a ruby Range object, I get this:
(0...5).each{|n| p n}
0
1
2
3
4
When I use the 'last' method I get:
(0...5).last
=> 5
I would have expected 4
Is this is a bug? Or is there something I don't understand about the the concept of a Range object?

This is by design. The Ruby 2.0 documentation is more specific:
Note that with no arguments last will return the object that defines the end of the range even if exclude_end? is true.
(10..20).last #=> 20
(10...20).last #=> 20
(10..20).last(3) #=> [18, 19, 20]
(10...20).last(3) #=> [17, 18, 19]

As Stefan has answered your observed behavior is expected and documented.
If you want to obtain the last element which would be enumerated by the range without having to enumerate the whole range, you could use Enumerable#reverse_each
irb> (0...5).reverse_each.first
=> 4

Related

array_object.uniq.each chain Ruby

In the code below I get the expected result:
(the point of the learning exercise I'm working on is to write code to modify the original array rather than returning a new array)
def filter_out!(array, &prc)
array.uniq.each { |el| array.delete(el) if prc.call(el) } #my block of code
end
arr_2 = [11, 17, 13, 15 ]
filter_out!(arr_2) { |x| x.odd? }
p arr_2 # []
However, if I remove the .uniq and only utilize array.each the output changes to [17, 15].
I believe the difference is that when only using array.each the index is being cycled through and when deleting 11 (because its odd) at the zero index, it looks at the next index, 1, but 17 is no longer at that index (13 is now) so the element is skipped for testing against the block. Same for 15 which is why it and 17 remain.
Is my assumption correct? If so, how does the underlying functionality of .uniq bypass this? I would assume that chaining .uniq in before .each would simply return the same 'incorrect answer' of [17, 15] since all values are already unique and .each would once again be performed on [11, 17, 13, 15 ] .
Is my assumption correct?
Yes.
How does the underlying functionality of .uniq bypass this?
Because calling this method returns a NEW OBJECT, so you're no longer mutating the object that's being iterated over.
# Your object_ids will be different!
arr_2.object_id
#=> 70302248117520
arr_2.uniq.object_id
#=> 70302210605760

getting differences between values in an array

I want to write an Array method in ruby that takes the successive values in the array and returns their differences as a new array (unshifting a '0' in at the beginning).
So feeding the array [4,7,11,16] into the method returns a new array [4,3,4,5].
1) does such a method already exist?
If not, then I think I know how to write it. However,
2) does a method already exist which allows me to test the input array and make sure it only consists of integers and/or floats?
Again, if not, I think I know how to write one.
p [4,7,11,16].unshift(0).each_cons(2).map{|a,b| b-a} # => [4, 3, 4, 5]
Keep it simple:
arr = [4,7,11,16]
last = 0
arr.map { |e| new=e-last; last=e; new }
#=> [4, 3, 4, 5]
Another way:
a = [arr.first]
enum = arr.each
loop do
a << -enum.next + enum.peek
end
a
#=> [4, 3, 4, 5]
Enumerator#peek raises a StopIteration exception when enum is at its last element. Kernel#loop handles the exception by breaking from the loop.
Regarding the first method, I am not aware of any such method in the Ruby Array class.
Regarding the second one, you can do it as explained in this answer:
your_array.all? {|i| i.is_a? Numeric }

Meaning of * in argument position

Assume arr is an array [[1,3],[2,5],[3,8]]. I was wondering what * means. What does it mean?
hash = Hash[*arr.flatten] # => {1=>3, 2=>5, 3=>8}
I tried the following
arr.flatten # => [1, 3, 2, 5, 3, 8]
Hash[arr.flatten] # => {}
Hash[*a.flatten] # => {1=>3, 2=>5, 3=>8}
Here is the explanation
When you do array.flatten it will give you one flatten array where all inner array slatted. Now You are putting this flatten array inside the Hash::[] method. But Hash::[] supports the below constructs :
Hash[ key, value, ... ] → new_hash
# or
Hash[ [ [key, value], ... ] ] → new_hash
Now array.flatten gives you [1, 3, 2, 5, 3, 8]. Now you are putting this array inside Hash[] like Hash[[1, 3, 2, 5, 3, 8]]. Now compare with the above 2 structures. Does either of them match ? NO. So you need to splat the inner array again, thus it need (splatted operator)* to splat the inner array.
Now you did Hash[*[1, 3, 2, 5, 3, 8]], which gives Hash[1, 3, 2, 5, 3, 8]. Now again check from the above 2 constructs. Does it match with either of the 2 ? This time YES, first one. So you got the desired hash {1=>3, 2=>5, 3=>8}.
BTW, you don't need to splat, as the second construt exactly matched when you put array inside Hash::[] directly.
array = [[1,3],[2,5],[3,8]]
Hash[array] # => {1=>3, 2=>5, 3=>8}
The above worked, because Hash[array] means Hash[[[1,3],[2,5],[3,8]]], which exactly the second structure as documentation suggested.
Read some examples to see how splatting work in Ruby.
There is another construct :-
Hash[ object ] → new_hash
This I think is also important to tell you why you got {}. Hash[[1, 3, 2, 5, 3, 8]] same as the last type of construct as per the doc. The doc is saying -
The second and third form take a single argument which is either an array of key-value pairs or an object convertible to a hash.
So. [1,3,2,5,3,8] it is an Array object not convertible to Hash. Currently it is giving you an empty hash, if an object as per the third construct. But it will throw error in future version of release. See Below warnings.
[1] pry(main)> Hash[[1,2]]
(pry):1: warning: wrong element type Fixnum at 0 (expected array)
(pry):1: warning: ignoring wrong elements is deprecated, remove them explicitly
(pry):1: warning: this causes ArgumentError in the next release
(pry):1: warning: wrong element type Fixnum at 1 (expected array)
(pry):1: warning: ignoring wrong elements is deprecated, remove them explicitly
(pry):1: warning: this causes ArgumentError in the next release
=> {}
My Ruby version is :
ruby 2.0.0p451 (2014-02-24 revision 45167) [i686-linux]
When you pass an argument to the Hash class you can put it in parentheses...
Hash(arr.flatten)
or without...
Hash arr.flatten
In either case, Hash takes the argument and if it's an array with an even number of elements, it will create a hash where the odd elements are the keys and the even elements are the values.
Hash can also take square brackets to preform a similar operation
Hash[1, 2]
=> {1=>2}
BUT, when you do this...
Hash[arr.flatten]
You are passing the array WITHIN the array so you're getting unexpected results.
When you do this...
Hash[*arr.flatten]
You are saying take the array arr.flatten and pass it, not as an array, but as individual separate arguments to the method.
So where Hash[arr.flatten] is actually Hash[[1, 3, 2, 5, 3, 8]] (an array containing one element which is an array) instead Hash[*arr.flatten] is actually Hash[1, 3, 2, 5, 3, 8](an array containing six elements)
Interestingly enough, the [] method can take an array containing arrays of key, value pairs so..
Hash[arr]
Works fine! You don't have to flatten or splat anything.

Ruby method overloading

So I have to implement two different situations. One is a method that multiplies two numbers, and can also multiply more than 2 numbers.
I'm using the following:
def multiply(arr)
arr.reduce(1, :*)
end
So far it works out fine if I unit test using an array input. Is there anyway to do this so my method can take in just two values, or an array, and return the relevant results? Is there also a way to implement this without even using an array input?
Use the splat operator:
def multiply(*arr)
arr.reduce(1, :*)
end
multiply(2, 3, 4, 5)
# => 120
If you want to also want to support input as an array, you can use flatten on arr:
def multiply(*arr)
arr.flatten.reduce(1, :*)
end
multiply([2, 3, 4, 5])
# => 120
multiply(10, 3, 5)
# => 150
multiply(10, 3)
# => 30

Ruby equivalent to Python's "array[i:]" to select all array elements after i?

I find myself wanting something like Python's
ary = [1,2,3,4,5,6,7,8]
ary[2:] #=> [3,4,5,6,7,8]
ALL of the time these days.
The solution always ends up being multi-lined and ugly. I'm wondering what the most elegant solutions out there might be, because mine are not worth showing.
Use Array#drop
2.1.0 :019 > ary.drop(2)
=> [3, 4, 5, 6, 7, 8]
You can write:
ary[2..-1]
# => [3,4,5,6,7,8]
-1 is the index of the last element in the array, see the doc for Array#[] for more informations.
A better alternative in Ruby is to use the Array#drop method:
ary.drop(2)
# => [3,4,5,6,7,8]

Resources