Find the 2nd element of array - ruby

I don't understand how this isn't working. The program is supposed to take instance method second in the class Array and return the 2nd object in the array
class Array
def second(*arr)
arr.length <= 1 ? nil : arr[1]
end
end
#Test cases
Test.assert_equals(Array([1, 2, 3]), 2,) #Getting nil
Test.assert_equals(Array([]), nil) #passes
Test.assert_equals(Array([1]), nil) #passes
What am I doing wrong? if I remove class Array and test on second it works fine?

Why use *arr? If you're monkey-patching Array, then use self:
class Array
def second
self.length <= 1 ? nil : self[1]
end
end
p [1,2,3].second #=> 2
p [1].second #=> nil
p [].second #=> nil

In answer to what you're doing wrong, your code as written doesn't need the splat (*) operator (it also doesn't need to be patched into the Array class). While patching into Array and using self allows you to call it like [1,2].second, you could also write it as follows without patching into Array:
def second(arr)
arr.length <= 1 ? nil : arr[1]
end
Which would need to be called like second([1,2]).
To find out more about the splat operator *, try something like this explanation (I confess - the first Google result, but seems ok), but what it's doing in your example is turning your passed-in array into an array of an array - e.g. [1,2,3] becomes [[1,2,3]].

Related

Using range.each vs while-loop to work with sequence of numbers in Ruby

Total beginner here, so I apologize if a) this question isn't appropriate or b) I haven't asked it properly.
I'm working on simple practice problems in Ruby and I noticed that while I arrived at a solution that works, when my solution runs in a visualizer, it gives premature returns for the array. Is this problematic? I'm also wondering if there's any reason (stylistically, conceptually, etc.) why you would want to use a while-loop vs. a for-loop with range for a problem like this or fizzbuzz.
Thank you for any help/advice!
The practice problem is:
# Write a method which collects all numbers between small_num and big_num into
an array. Ex: range(2, 5) => [2, 3, 4, 5]
My solution:
def range(small_num, big_num)
arr = []
(small_num..big_num).each do |num|
arr.push(num)
end
return arr
end
The provided solution:
def range(small_num, big_num)
collection = []
i = small_num
while i <= big_num
collection << i
i += 1
end
collection
end
Here's a simplified version of your code:
def range(small_num, big_num)
arr = [ ]
(small_num..big_num).each do |num|
arr << num
end
arr
end
Where the << or push function does technically have a return value, and that return value is the modified array. This is just how Ruby works. Every method must return something even if that something is "nothing" in the form of nil. As with everything in Ruby even nil is an object.
You're not obligated to use the return values, though if you did want to you could. Here's a version with inject:
def range(small_num, big_num)
(small_num..big_num).inject([ ]) do |arr, num|
arr << num
end
end
Where the inject method takes the return value of each block and feeds it in as the "seed" for the next round. As << returns the array this makes it very convenient to chain.
The most minimal version is, of course:
def range(small_num, big_num)
(small_num..big_num).to_a
end
Or as Sagar points out, using the splat operator:
def range(small_num, big_num)
[*small_num..big_num]
end
Where when you splat something you're in effect flattening those values into the array instead of storing them in a sub-array.

How can I do the opposite .include?

So the goal here is to print the index of the element if the element is in the array or print -1 if the element is not in the array. I have to do this using loops. PLEASE HELP!
def element_index(element, my_array)
while my_array.map.include? element do
puts my_array.index(element)
break
end
until my_array.include? element do
puts -1
break
end
end
p element_index("c", ["a","b","c"])
If it's OK to use Array#index, then
def element_index(elem, collection)
collection.index(elem) || -1
end
Or if it's a homework that you should not use Array#index, or you want to do this on arbitrary collections, then
def element_index(elem, collection)
collection.each_with_index.reduce(-1) do |default, (curr, index)|
curr == elem ? (return index) : default
end
end
By the way, I always turn to Enumerable#reduce when I want to iterate over a collection (array, map, set, ...) to compute one value.
This is an easy way but maybe it doesn't meet the criteria for "using loops":
def element_index(x, arr)
arr.index(x) || -1
end
element_index("c", ["a","b","c"]) #=> 2
element_index("d", ["a","b","c"]) #=> -1
To explicitly use a loop:
def element_index(x, arr)
arr.each_index.find { |i| arr[i] == x } || -1
end
As pointed out in the comments, we could instead write
arr.each_index.find(->{-1}) { |i| arr[i] == x }
element_index("c", ["a","b","c"]) #=> 2
element_index("d", ["a","b","c"]) #=> -1
I know this is an assignment, but I'll first cover this as if it were real code because it's teaching you some not-so-great Ruby.
Ruby has a method for doing this, Array#index. It returns the index of the first matching element (there can be more than one), or nil.
p ["a","b","c"].index("c") # 2
p ["a","b","c"].index("d") # nil
Returning -1 is inadvisable. nil is a safer "this thing does not exist" value because its never a valid value, always false (-1 and 0 are true in Ruby), and does not compare equal to anything but itself. Returning -1 indicates whomever came up with this exercise is converting it from another language like C.
If you must, a simple wrapper will do.
def element_index(element, array)
idx = array.index(element)
if idx == nil
return -1
else
return idx
end
end
I have to do this using loops.
Ok, it's homework. Let's rewrite Array#index.
The basic idea is to loop through each element until you find one which matches. Iterating through each element of an array is done with Array#each, but you need each index, that's done with Array#each_index. The element can be then gotten with array[idx].
def index(array, want)
# Run the block for each index of the array.
# idx will be assigned the index: 0, 1, 2, ...
array.each_index { |idx|
# If it's the element we want, return the index immediately.
# No need to spend more time searching.
if array[idx] == want
return idx
end
}
# Otherwise return -1.
# nil is better, but the assignment wants -1.
return -1
end
# It's better to put the thing you're working on first,
# and the thing you're looking for second.
# Its like verb( subject, object ) or subject.verb(object) if this were a method.
p index(["a","b","c"], "c")
p index(["a","b","c"], "d")
Get used to using list.each { |thing| ... }, that's how you loop in Ruby, along with many other similar methods. There's little call for while and for loops in Ruby. Instead, you ask the object to loop and tell it what to do with each thing. It's very powerful.
I have to do this using loops.
You approach is very creative. You have re-created an if statement using a while loop:
while expression do
# ...
break
end
Is equivalent to:
if expression
# ...
end
With expression being something like array.include? element.
How can I do the opposite?
To invert a (boolean) expression, you just prepend !:
if !expression
# ...
end
Applied to your while-hack:
while !expression do
# ...
break
end
The whole method would look like this:
def element_index(element, my_array)
while my_array.include? element do
puts my_array.index(element)
break
end
while !my_array.include? element do
puts -1
break
end
end
element_index("c", ["a","b","c"])
# prints 2
element_index("d", ["a","b","c"])
# prints -1
As I said at the beginning, this approach is very "creative". You are probably supposed to find the index using a loop (see Schwern's answer) instead of calling the built-in index.

Class inheritance (from Array) and custom methods in Ruby

I'm up against something I don't quite understand.
Here's the class I've created, and the driver code I'm running to test my method:
class Dog < Array
def breathing?
self.length > 1
end
end
the_dog = Dog.new(["Arf", "Woof"])
puts the_dog.breathing?
# true
the_dog.shift
puts the_dog.breathing?
# false
How come, when I call #shift on the_dog, the_dog stops breathing?
Shift method returns the first element of self and removes it (shifting all other elements down by one). Returns nil if the array is empty.
a = [1,2,3]
a.shift
=> [2,3]
So, In your case the first argument of the array is removed. Once its removed, the condition the_dog.breathing? is returned false.

odd usage of "end" in Sample code

Looking through this I notice something I have never seen before on line 83.end.map(&:chomp) so end is an object? (I realize that might be 100% wrong.) Can someone explain what and how that works there? What exactly is advantage?
No, end is not an object, but object.some_method do ... end is an object (or rather it's evaluated to an object) - namely the object returned by the some_method method.
So if you do object.some_method do ... end.some_other_method, you're calling some_other_method on the object returned by some_method.
The full code snippet you're referring to is below:
def initialize(dict_file)
#dict_arr = File.readlines(dict_file).select do |word|
!word.include?("-") && !word.include?("'")
end.map(&:chomp)
end
notice that the end you're talking about is the end of the block that starts on the 2nd line (it matches the do on line 2).
Perhaps if you see it parenthesized, and rewritten with curly braces, it will make more sense:
def initialize(dict_file)
#dict_arr = (File.readlines(dict_file).select { |word|
!word.include?("-") && !word.include?("'")
}).map(&:chomp)
end
It's often helpful to examine what Ruby is doing, step-by-step. Let's see what's going with the method ComputerPlayer#initialize:
def initialize(dict_file)
#dict_arr = File.readlines(dict_file).select do |word|
!word.include?("-") && !word.include?("'")
end.map(&:chomp)
end
First, create a file:
File.write("my_file", "cat\ndog's\n")
When we execute:
ComputerPlayer.new("my_file")
the class method IO#readlines is sent to File, which returns an array a:
a = File.readlines("my_file")
#=> ["cat\n", "dog's\n"]
Enumerable#select is sent to the array a to create an enumerator:
b = a.select
#=> #<Enumerator: ["cat\n", "dog's\n"]:select>
We can convert this enumerator to an array to see what it will pass to it's block:
b.to_a
=> ["cat\n", "dog's\n"]
The enumerator is invoked by sending it the method each with a block, and it returns an array c:
c = b.each { |word| !word.include?("-") && !word.include?("'") }
#=> ["cat\n"]
Lastly, we send Enumerable#map with argument &:chomp (the method String#chomp converted to a proc) to the array c:
c.map(&:chomp)
#=> ["cat"]
A final point: you can improve clarity by minimizing the use of !. For example, instead of
...select do |word|
!word.include?("-") && !word.include?("'")
consider
...reject do |word|
word.include?("-") || word.include?("'")
You might also use a regex.

Check if array element data[i][j][k] exists

I need to find out if data[i][j][k] element exists, but I don't know if data[i] or data[i][j] not nil themselves.
If I just data[i][j][k].nil?, it throw undefined method [] for nil:NilClass if data[i] or data[i][j] is nil
So, I am using now
unless data[i].nil? or data[i][j].nil? or data[i][j][k].nil?
# do something with data[i][j][k]
end
But it is somehow cumbersome.
Is there any other way to check if data[i][j][k] exists without data[i].nil? or data[i][j].nil? or data[i][j][k].nil? ?
I usually do:
unless (data[i][j][k] rescue false)
# do something
end
Here are three different alternatives:
Shorten it
You can shorten it slightly by using "!" instead of .nil?:
!data[i] or !data[i][j] or !data[i][j][k]
You could get rid of the repetition by doing this:
((data[i] || [])[j] || [])[k].nil?
Abstract away these details
Both of the code snippets above are nasty enough that I would probably not write them more than once in a code base.
A three-dimensional array seems complicated enough that you shouldn't be accessing it directly in lots of places in your code. You should consider wrapping it inside an object with an appropriate name:
class My3DWorld
def initialize
# set up #data
end
# Gets the point or returns nil if it doesn't exist.
def get_point(i, j, k)
#data[i] && #data[i][j] && #data[i][j][k]
end
end
Use a hash instead
However, ultimately, I wonder whether you really need a 3D array. Another more Ruby-like way to implement this data structure would be to use a hash and use i,j,k coordinate tuples as the keys. Unless this is a huge structure and you need the performance characteristics of a 3D array, I recommend looking at my other answer here:
https://stackoverflow.com/a/20600345/28128
The new feature "refinements" is an option:
module ResponsiveNil
refine NilClass do
def [](obj)
nil
end
end
end
using ResponsiveNil
a = [[1]]
p a[2][3][4] #=> nil
You can shorten slightly to
if data[i] && data[i][j] && data[i][j][k]
# do something with data[i][j][k]
end
You can also you the "andand" gem which allows you to write:
data[i].andand[j].andand[k]
If you are willing to monkey patch Array, you could define a method to enable this, such as:
class Array
def index_series(*args)
result = self
args.each do |key|
result = result[key]
return nil if result.nil?
end
result
end
end
which would let you do:
data.index_series(i, j, k)
The following permits any amount of nesting, and allows for the possibility that an element of the array has a value of nil:
def element_exists?(arr, *indices)
if arr.is_a? Array
return true if indices.empty?
return false if arr.size <= (i = indices.pop)
element_exists?(arr[i], *indices)
else
indices.empty?
end
end
data = [[0,1],[2,nil]]
element_exists?(data) # => true
element_exists?(data, 1) # => true
element_exists?(data, 2) # => false
element_exists?(data, 1, 1) # => true
element_exists?(data, 1, 2) # => false
element_exists?(data, 1, 1, 1) # => false

Resources