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.
Related
During my lessons of Ruby I came across of this exercise. I'm trying to remove 3 or more the same charactes in a row. Test Cases Input: abbbaaccada Output: ccada Input: bbccdddcb Output: (Empty string)
So far I have solution which doesn't return expected results:
def playground("abbbaaccada")
count = string.length
string.chars.each_with_index.map { |v, i| (v * (count - i)).capitalize }.join('')
end
output gives me
==> AaaaaaaaaaaBbbbbbbbbbBbbbbbbbbBbbbbbbbAaaaaaaAaaaaaCccccCcccAaaDdA
instead of
==> ccada
Could you please advise?
Edit:
Forgot to add that regexp isn't allowed
There are two challenges here:
Match and remove any run of thee or more characters in a row
Recurse to test again in case the previous step created a new run of three
Here's one way to do it:
THREE_OR_MORE = /(.)\1{2,}/
def three_is_too_many(str)
if str.match? THREE_OR_MORE
str = three_is_too_many(str.gsub(THREE_OR_MORE, ''))
end
str
end
The regexp finds any character ('.'), followed by itself ('\1'), two or more times ('{2,}').
Then the routine either a) removes three or more and tests again or b) returns the string.
Here's a potential solution. The following method searches for any subsequence of an array with repeats, and returns the range of the repeated values if there are three or more of them.
def find_3_or_more(ary)
ary.each_index do |i|
j = i + 1
while j < ary.length && ary[i] == ary[j]
j += 1
end
return (i...j) if j - i > 2
end
nil
end
This portion breaks the target string into an array of chars, and repeatedly slices out the characters in ranges identified as repeats until there are none, as indicated by a nil range.
def delete_3_or_more(str)
ary = str.chars
while r = find_3_or_more(ary)
ary.slice!(r)
end
return ary.join
end
It seems to do the job for your test cases.
def recursively_remove_runs_of_3_or_more(str)
arr = str.chars
loop do
a = arr.slice_when { |a,b| a.downcase != b.downcase }.to_a
b = a.reject! { |e| e.size > 2 }
arr = a.flatten
break arr.join if b.nil?
end
end
recursively_remove_runs_of_3_or_more "abbbaaccada"
#=> "ccada"
This uses Enumerable#slice_when (new in MRI v2.2). Note that Array#reject! returns nil when no changes are made.
You could alternatively use Enumerable#chunk_while (new in MRI v2.3). Simply replace:
a = arr.slice_when { |a,b| a.downcase != b.downcase }.to_a
with:
a = arr.chunk_while { |a,b| a.downcase == b.downcase }.to_a
chunk_while and slice_when are yin and yang.
If a regular expression could be used and case where not an issue, you could write:
str = "abbbaaccada"
s = str.dup
loop { break(s) if s.gsub!(/(.)\1{2,}/, '').nil? }
#=> "ccada"
(I wanted to comment, but it doesn't allow me to do that yet.)
Assuming that you are trying to learn, I chose to only give you some tips while avoiding a solution.
There might be shorter ways of doing this using regex or/and some String methods. However, you said you can not use regex.
My tip is, try to solve it only using the sections you have covered so far. It may not necessarily be the most elegant solution, but you can revise it as you progress. As others suggested, recursion might be a good option. But, if you are not familiar with that yet, you can try slicing the string and merging the parts you need. This can be combined with an endless loop to check the new string satisfies your condition: but think about when you need to break out of the loop.
Also, in your code:
v * (count - i)
String#* actually gives you count - i copies of v, concatenated together.
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.
When using an accumulator, does the accumulator exist only within the reduce block or does it exist within the function?
I have a method that looks like:
def my_useless_function(str)
crazy_letters = ['a','s','d','f','g','h']
str.split.reduce([]) do |new_array, letter|
for a in 0..crazy_letters.length-1
if letter == crazy_letters[a]
new_array << letter
end
end
end
return true if (new_array == new_array.sort)
end
When I execute this code I get the error
"undefined variable new_array in line 11 (the return statement)"
I also tried assigning the new_array value to another variable as an else statement inside my reduce block but that gave me the same results.
Can someone explain to me why this is happening?
The problem is that new_array is created during the call to reduce, and then the reference is lost afterwards. Local variables in Ruby are scoped to the block they are in. The array can be returned from reduce in your case, so you could use it there. However, you need to fix a couple things:
str.split does not break a string into characters in Ruby 2+. You should use str.chars, or str.split('').
The object retained for each new iteration of reduce must be retained by returning it from the block each time. The simplest way to do this is to put new_array as the last expression in your block.
Thus:
def my_useless_function(str)
crazy_letters = ['a','s','d','f','g','h']
crazy_only = str.split('').reduce([]) do |new_array, letter|
for a in 0..crazy_letters.length-1
if letter == crazy_letters[a]
new_array << letter
end
end
new_array
end
return true if (crazy_only == crazy_only.sort)
end
Note that your function is not very efficient, and not very idiomatic. Here's a shorter version of the function that is more idiomatic, but not much more efficient:
def my_useless_function(str)
crazy_letters = %w[a s d f g h]
crazy_only = str.chars.select{ |c| crazy_letters.include?(c) }
crazy_only == crazy_only.sort # evaluates to true or false
end
And here's a version that's more efficient:
def efficient_useless(str)
crazy_only = str.scan(/[asdfgh]/) # use regex to search for the letters you want
crazy_only == crazy_only.sort
end
Block local variables
new_array doesn't exist outside the block of your reduce call. It's a "block local variable".
reduce does return an object, though, and you should use it inside your method.
sum = [1, 2, 3].reduce(0){ |acc, elem| acc + elem }
puts sum
# 6
puts acc
# undefined local variable or method `acc' for main:Object (NameError)
Your code
Here's the least amount of change for your method :
def my_useless_function(str)
crazy_letters = ['a','s','d','f','g','h']
new_array = str.split(//).reduce([]) do |new_array, letter|
for a in 0..crazy_letters.length-1
if letter == crazy_letters[a]
new_array << letter
end
end
new_array
end
return true if (new_array == new_array.sort)
end
Notes:
return isn't needed at the end.
true if ... isn't needed either
for loop should never be used in Ruby
reduce returns the result of the last expression inside the block. It was for in your code.
If you always need to return the same object in reduce, it might be a sign you could use each_with_object.
"test".split is just ["test"]
String and Enumerable have methods that could help you. Using them, you could write a much cleaner and more efficient method, as in #Phrogz answer.
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]].
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