I am trying to search an array for a substring and if that substring exists, shovel it into a new array. The problem I am having is that it keeps coming back with this error message:
`block in substrings': undefined method `<<' for nil:NilClass
I have verified that the index in the method is not nil by printing it. I have also done index == nil to double check.
What am I missing here?
Thanks in advance for your help!
new_array = []
def substrings(word, array)
new_array = array.each do |index|
if index.include? (word)
p index
p index == nil
new_array << index
end
end
end
dictionary = ["below", "down", "go", "going", "horn", "how", "howdy", "it", "i", "low", "own", "part", "partner", "sit"]
substrings("i", dictionary)
You basically combine two different ways of solving this problem. The first is to assign the new_array the result of looping though the array, but in that case, the new_array variable is not available to use inside the block.
So you could either choose to create the variable first, like this
new_array = []
array.each do |index|
if index.include?(word)
new_array << index
end
end
Alternatively you could use a method called reduce which takes a more functional programming approach. That could look like this
new_array = array.reduce([]) do |arr, index|
if index.include?(word)
arr << index
else
arr
end
end
What reduce does is that the block argument arr is always set to the return value of the previous block execution. that can make the syntax a little longer than it has to be, so Ruby also has an alternate approach to reduce, called each_with_object, that does the same, but by mutating the same variable, instead of requiring a return value. I actually prefer this way and would solve it like this.
new_array = array.each_with_object([]) do |index, arr|
arr << index if index.include?(word)
end
I would like to extend a bit the (correct) answer given by #DanneManne: While it is correct that you can access local variables from outer blocks from within an inner block, you can't do it within a def, and it is unnecessary in your example, because you can initialize new_array inside the body of your method and return it as result. But in case you ever really need this kind of construct, there indeed is a workaround:
So, this does NOT work:
a=5
def f
puts a; # WRONG. a is not known here
end
and this works different than you seem to expect:
a=5
def f
a=6
end
puts a # prints 5
f
puts a # prints 5 again
But if you define your method like this, it works:
a=5
define_method(:f) do
puts a; # OK, refers to outer variable a
end
By using a block, you create a closure with this, so if you do now a
f;
a=6;
f
5 and 6 is printed, in this order.
I have verified that the index in the method is not nil by printing it. I have also done index == nil to double check.
What am I missing here?
Not index is nil, but new_array. The error message says:
undefined method `<<' for nil:NilClass
and refers to the line new_array << index. Here, << is the method and new_array is the receiver. And for some reason, new_array is nil.
You probably expected new_array to be [] because you explicitly said new_array = []. But methods have their own local variable scope. If you define a local variable outside of a method, it won't be available inside, or vice-versa.
Typically when referring to an undefined variable you'd get:
undefined local variable or method `new_array'
but here, the 2nd assignment conceals the actual problem:
new_array = array.each do |index|
^^^^^^^^^^^
When Ruby encounters this line, it immediately creates a local variable new_array with an initial value of nil. (local variables are created when the line is parsed, not when the assignment occurs)
To get the expected result, you have to move new_array = [] into the method, get rid of the new_array = array.each { ...} assignment and return new_array at the end:
def substrings(word, array)
new_array = []
array.each do |index|
if index.include?(word)
new_array << index
end
end
new_array
end
The variable names are a bit arbitrary, maybe even misleading. Having index.include?(word) looks like you're comparing a numerical index to a string. I'd use something like this:
def substrings(substring, words)
result = []
words.each do |word|
if word.include?(substring)
result << word
end
end
result
end
Code-wise, you can incorporate the array into the loop via each_with_object which will also return the array:
def substrings(substring, words)
words.each_with_object([]) do |word, result|
if word.include?(substring)
result << word
end
end
end
However, selecting elements based on a condition is such a common task that Ruby provides a dedicated method select – you merely have to return true or false from the block to indicate whether the element should be selected:
def substrings(substring, words)
words.select do |word|
word.include?(substring)
end
end
Related
I'm not sure what I am missing by looking at these three functions.
The desired result for these functions would be that the original array changes value so
change_this = [6, 7, 8]
array_times_two!(change_this)
change_this == [12, 14, 16] => true
The following function does this
def array_times_two!(array)
array.map! {|x| x * 2}
end
And so does this one...
def array_times_two!(array)
array.each_with_index do |element, index|
array[index] = array[index] * 2
end
array
end
How come the following function does not change the value like the others?
def array_times_two!(array)
array = array.map {|x| x * 2}
array
end
How is the second function changing the array but the third isn't?
In the last example you have array coming in as a local variable. Reassigning the local variable has zero effect on the original variable. This is because Ruby is, in effect, pass by value, except the confusing part is those values are often object references, or a fancy term for pointers.
The consequences of this aren't that complicated though: Calling a method on an object can modify the contents of that object. Reassigning a variable cannot.
The first version of your method is probably the best, but you could make it more generic, like:
def multiply_by!(array, n = 2)
array.map! { |v| v * n }
end
You can also "fix" the last version by using a method call to apply the changes:
def array_times_two!(array)
array.replace(array.map {|x| x * 2})
end
This calls Array#replace to stomp the contents of the original object and force the changes to stick. It's not a very elegant solution when things like map! exist.
For a challenge, I'm trying to return the unique values of an array without using uniq. This is what I have so far, which doesn't work:
def unique
unique_arr = []
input_arr.each do |word|
if word != unique_arr.last
unique_arr.push word
end
end
puts unique_arr
end
input = gets.chomp
input_arr = input.split.sort
input_arr.unique
My reasoning here was that if I sorted the array first before I iterated through it with each, I could push it to unique_arr without repetition being a possibility considering if it's a duplicate, the last value pushed would match it.
Am I tackling this the wrong way?
Yes, you are making at least two mistakes.
If you want to call it as input_arr.unique with input_arr being an array, then you have to define the method on Array. You have input_arr within your method body, which comes from nowhere.
puts in the last line of your code outputs to the terminal, but makes the method return nil, which makes it behave differently from uniq.
It can be fixed as:
class Array
def unique
unique_arr = []
each do |word|
unique_arr.push(word) unless unique_arr.last == word
end
unique_arr
end
end
A unique array? That sounds like a Set to me:
require 'set'
Set.new([1,2,3,2,3,4]).to_a
#=> [1,2,3,4]
Here's a concise way to do it that doesn't explicitly use functionality from another class but probably otherwise misses the point of the challenge:
class Array
def unique
group_by(&:itself).keys
end
end
I try this three options. Just for challenge
class Array
def unique
self.each_with_object({}) { |k, h| h[k] = k }.keys
end
def unique2
self.each_with_object([]) { |k, a| a << k unless a.include?(k) }
end
def unique3
arr = []
self.map { |k| arr << k unless arr.include?(k) }
arr
end
end
Here is one more way to do this:
uniques = a.each.with_object([]) {|el, arr| arr << el if not arr.include?(el)}
That's so easy if you see it this way:
a = [1,1,2,3,4]
h = Hash.new
a.each{|q| h[q] = q}
h.values
and this will return:
[1, 2, 3, 4]
With the puts command below I output the contents of my variable new_array and its contents at index 0, which I also have stored in a variable called first element. I then output the class of first_element and I get a nil class returned. Can someone explain why this is? Also, is there a better way to do this?
Thanks!
new_array = []
first_element = new_array[0]
array = ["1", "2"]
array.each do |x|
new_array << x.to_i
end
puts new_array[0]
puts first_element.class
output
$ruby testing_2.rb
1
NilClass
When new_array is initialized, it's initialized as an empty array. That means it has zero elements. If you try to access an index, you're trying to access an index that doesn't exist. In Ruby, this is referred to as nil.
You are taking the first value of a blank array, which does not exist, so it becomes nil. NilClass is simply the class name for a nil object.
To make it not nil, you need to put something in your new_array, such as ["foo","bar",3.14]. Then, the first_element will be "foo", which is the String class.
class Array
define_method(:trim) do
new_array = self.pop()
new_array = self.shift()
end
end
EDIT: What I tried (among other things)
["hi", "ho", "he"].trim()
This returns "hi".
Remove the last element. Remove the first element. But how do I get the method to return the remaining array instead of what's returned by .shift (or whatever happens to be the last instruction of the method)? Do I need another variable?
Thank you.
pop() and shift() will modify the array directly. You just need to tell the method to return self
class Array
define_method(:trim) do
self.pop()
self.shift()
self
end
end
EDIT : as this method can be dangerous, I suggest you define both trim! and trim. Where trim! will modify the array directly and trim return a copy of the array, trimmed
class Array
def trim!
self.pop()
self.shift()
self
end
def trim
self.dup.trim!
end
end
You can use range when accessing array elements, like that
ary = [1, 2, 3, 4]; ary[1..-2] #=> [2, 3]
So going back to the method, it can be:
class Array
def trim
self[1..-2]
end
end
[EDIT]: to avoid returning nil for empty arrays:
class Array
def trim
self[1..-2] || []
end
end
Trying to make a method skip_animals that takes an animals array and a skip integer and returns an array of all elements except first skip number of items.
input: skip_animals(['leopard', 'bear', 'fox', 'wolf'], 2)
expected output: ["2:fox", "3:wolf"]
def skip_animals(animals, skip)
arr = Array.new
animals.each_with_index{|animal, index| arr.push("#{animal}:#{index}") }
puts arr.drop(skip)
end
This instead puts each output on a separate line and doesn't add them to the array arr. I thought the arr.push would add them correctly. What do I have to do to get the elements added to the array?
I want to use these methods, not map or something more advanced. I need to tinker with this each_with_index line, not overhaul it.
(This is a challenge on Hackerrank, so it uses STDIN and STDOUT)
EDIT
Here is my updated code with p instead of puts. It's giving me a weird output of two different arrays, not sure why.
def skip_animals(animals, skip)
arr = Array.new
animals.each_with_index{|animal, index| arr.push("#{index}:#{animal}") }
p arr.drop(skip)
end
This gives me two lines of output:
["3:panda", "4:tiger", "5:deer"]
["0:leopard", "1:bear", "2:fox", "3:wolf", "4:dog", "5:cat"]
I'm assuming the top is the correct array, but I don't get why the second is printing also, or why it has a different set of animals.
Use p instead of puts.
irb(main):001:0> puts ['1', '2']
1
2
=> nil
irb(main):002:0> p ['1', '2']
["1", "2"]
According to the documentation, puts:
Writes the given objects to ios as with IO#print. Writes a record
separator (typically a newline) after any that do not already end with
a newline sequence. If called with an array argument, writes each
element on a new line. If called without arguments, outputs a single
record separator.
BTW, I would code like this (using Enumerable#map + returning result instead of printing inside the function):
def skip_animals(animals, skip)
animals.drop(skip).each_with_index.map { |animal, index|
("#{index + skip}:#{animal}")
}
end
p skip_animals(['leopard', 'bear', 'fox', 'wolf'], 2)
just remove puts remove form this line puts arr.drop(skip)
def skip_animals(animals, skip)
arr = Array.new
animals.each_with_index{|animal, index| arr.push("#{animal}:#{index}") }
arr.drop(skip)
end