Adding items to a new array with index - arrays

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

Related

Can't shovel string into new array in Ruby

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

Print elements of ruby array line by line after splitting a single string with "\n" condition

I have a single string, achieved using backticks of the following form:
output = `git log`
then, I have splitted the result where there are "\n" and the result went into an array of the form:
array = output.split("\n")
then, I am just trying to output the result in the screen, however, when I am using
array.each do |a|
puts a
end
I am getting as a result a double line of the form:
result after puts
(empty line)
result after puts etc
when my preferred result is a single line of the form:
result after puts
result afters puts etc
I tried to perform this with print, but I am getting:
result after puts result after puts etc
in a single line.
Can you please help me?
The issue is when you split using \n, if there are two \n characters then an empty "" gets added to the array.
eg: test = ["this","","is","test"]
Now if you do,
test.each do |a|
puts a
end
The o/p will be,
this
// a new line will come here.
is
test
so you should reject the empty values,
test2 = test.reject{ |value| value == ""}
test2.each do |a|
puts a
end
Result is,
this
is
test
In same way,
output = `git log`
array = output.split("\n")
array2 = array.reject{ |value| value == ""}
array2.each do |a|
puts a
end
This will give you the correct result.
Thanks to #AndreyDeineko, we have that:
"The issue is when you split using \n if there are two \n characters then an empty "" gets added to the array. Howcome? a = "1\n2\n3\n4\n"; a.split("\n") #=> ["1", "2", "3", "4"].
Therefore, array.each { |a| a } will work for you"
It did not work 100% for me, but using his answer, I manage to achieve the required result which is:
array.each { |a| a }
puts array

Sort method in Ruby

I wrote method, to sort array of user inputs(integers) from low to high and reverse. Code looks like:
def alphabetize(arr, rev=false)
arr.sort!
if rev==true
arr.reverse!
end
return arr
end
puts "enter your numbers with 'space' between them"
text = gets.chomp
numbers = text.split(" ")
numbers.each do |element|
element.to_i
end
puts alphabetize(numbers)
First time i tried only numbers from 0 to 9, and method had worked correct. But then i tried to input numbers like 1112, 11, 22 and after sorting procedure, i had got result like this "11 1112 22". After this, i tried to change code this way:
def alphabetize(arr, rev=false)
arr.sort!{|a, b| a.to_i <=> b.to_i}
if rev==true
arr.reverse!
end
return arr
end
puts "enter your numbers with 'space' between them"
text = gets.chomp
numbers = text.split(" ")
puts alphabetize(numbers)
And this way my code works correct and sorting input of 11, 1112, 22 produce the result that i expect: "11, 22, 1112". Looks like i have solved the problem, but i really want to understand the difference between first version and second version of code. Why method sort works in different ways?
each does not replace the elements in an array - it simply returns the array itself (without change).
You might prefer to use map:
numbers = numbers.map do |element|
element.to_i
end
Or, better yet, use map!, which actually changes the array itself, rather than return the changed array:
numbers.map! do |element|
element.to_i
end
You can also use the following shorthand:
numbers.map! &:to_i
Looks like in the first place you still comparing strings instead of integers.
In fact, in the first place you don't convert elements to integers:
numbers.each do |element|
element.to_i
end
The script below don't replace strings with integers in array.

Print contents of array in oneline

I am new to ruby and was trying out with arrays.i want to print the array in single line.
this is the block of code(please ignore any errors)
array=[]
puts "Choose an option: ","1.Push, 2.Pop, 3.Display Length"
choice=gets.to_i
while choice!=4
if choice==1
puts "enter Number of elements to be pushed"
n=gets.to_i
n.times do
puts "Enter element"
el=gets.to_s
array.push el
end
puts array
elsif choice==2
puts array.pop
elsif choice==3
puts array.length
else
puts "invalid"
end
end
when I print my array in if choice==1i get all the outputs on different lines,
example
hello
i
am
beginner
to
ruby
is there anyway to put the output in single line?
i.e hello i am beginner to ruby
EDIT: I have even tried using puts array.join(' '), but that too doesnt work.
First of all,
puts array
should be
puts array.join(' ')
By default, puts outputs each element on its own line.
Secondly,
el=gets.to_s
should be
el = gets.chomp
gets returns a string, so there's not much point in converting a string to a string. But the string returned by gets will also end with a newline, so you need to chomp that newline off.
Instead of puts array try p array if you want the whole array printed to screen.
New to Ruby as well! I just tried:
puts "#{array.inspect}"
where array = [2 3 4 5 6 77 88]. It resulted in:
["2", "3", "4", "5", "6", "77", "88"]
which is a single line printout, if a quick fix is needed. But array.join(' ') is much better.

Define a trim method on Array that removes the first and last element

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

Resources