I am a programming neophyte so I apologize if this is more simple than it seems. I've searched around and only found individual answers for each_with_index and conditions, never together.
My goal is to iterate through an array and perform a single action based on the index. My first draft looked a little something like this:
#word_array.each_with_index do |word, index|
if index % 2 == 0
word = "EVEN"
end
end
Which works, but then I wanted to refactor it into a single line. My first attempt invoked an error due to it anticipating the else : part of the condition:
#word_array.each_with_index { |word, index| index % 2 == 0 ? word = "EVEN" }
I had tried it this way first thinking I may have some luck being as the block did not require an else condition, but obviously to no avail. I did discover I could use nil as a placeholder and have the code work:
#word_array.each_with_index { |word, index| index % 2 == 0 ? word = "EVEN" : nil }
However even after further reading up on each piece I'm uncertain whether or not having the nil there could potentially cause troubles in more complex situations even though it doesn't hamper my basic example here.
So to break it down into two questions:
1.) Is the nil going to bite me in the butt later?
2.) If so, is there a way to go about this using one line, or is my best bet to stick with the conditional block?
If you want this one-liner style:
word = 'EVEN' if index % 2 == 0
Using a ternary with a missing condition leaves people wondering why you didn't do this in the first place. A ternary is a great way to branch, like:
(index % 2 == 0) ? 'EVEN' : word
#word_array.each_with_index { |word, index| (index % 2 == 0) ? word = "EVEN" : nil }
should work. The ternary operator is easily confused.
1.) Is the nil going to bite me in the butt later?
Yes. Why not:
#word_array.each_with_index { |word, index| word = (index % 2 == 0) ? "EVEN" : "ODD" }
Related
I am trying to solve HackerRank's 'New year Chaos' challenge.
The script is supposed to print 'Too chaotic' in a certain case.
Regardless of wether or not my solution is correct, my current problem is that I can't seem to print it as it will then return nil. My solution is not accepted either it I replace it with a puts as it will include quotes.
bribes = 0
chaotic = false
q.each_with_index do |num, index|
if num - 1 - index > 2
chaotic = true
elsif index == 0
else
bribes += q.slice(0, index).count { |x| x > num }
end
end
return chaotic ? print('Too chaotic') : bribes
end
Hacker rank output
thanks!
Interesting problem to tackle. The main difference between print and puts is the included newline with puts. You want to use puts in this case. Here's a good discussion on the topic: https://www.rubyguides.com/2018/10/puts-vs-print/
I'm not sure what was happening with the quotes you mention, so I'd update your solution with puts, and tackle the quotes next.
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.
I was recently discussing the following Ruby syntax with a colleague:
value = if a == 0
"foo"
elsif a > 42
"bar"
else
"fizz"
end
I haven't seen this sort of logic much personally, but my colleague notes that it's actually a fairly common Rubyism. I tried googling the topic and found no articles, pages, or SO questions discussing it, making me believe it might be a very matter-of-fact technique. Another colleague, however, finds the syntax confusing and would instead write the above logic like this:
if a == 0
value = "foo"
elsif a > 42
value = "bar"
else
value = "fizz"
end
The disadvantage there being the repeated declarations of value = and the loss of an implicit else nil, if we wanted to use it. This also feels like it lines up with a lot of other syntactical sugar features found in Ruby.
My question is, how common is this technique in Ruby? Is there some sort of consensus on whether the community feels like this should be used or avoided?
value = if condition
x
else
y
end
is common. It lends itself to cleaning up this situation:
if condition
value = x
else
value = y
end
Have a look at this Ruby style guide. It's a popular guide in how Ruby code should be written.
https://github.com/bbatsov/ruby-style-guide#use-if-case-returns
The fact that if and case return values makes for some very tight, tidy, and yet still understandable code. It's a common pattern in Ruby when you're dealing with assignment through branching.
The way I tend to approach formatting these is to apply a level of indentation to make the assignment clear, but not overly "push" the code in too far:
value =
if a == 0
"foo"
elsif a > 42
"bar"
else
"fizz"
end
Or if you want a case:
value =
case
when a == 0
"foo"
when a > 42
"bar"
else
"fizz"
end
In many cases you'll see a method that has an if as the body to determine the result:
def value(a)
if a == 0
"foo"
elsif a > 42
"bar"
else
"fizz"
end
end
Then there's no quirky indentation necessary.
I would like to make a program that checks to see if the number you enter is an even number. Sort of like making a leap year program but for any number divisible by 2.
Something along the lines of:
num = gets.chomp
while num != 0
if (num%2) == 0
puts 'yess'
else
puts 'nooo'
end
end
I knows there's something easy that I need to change for it to run.
(btw I just started learning Ruby yesterday!)
There are two problems here.
First being something that others have put, you need to make sure you turn the input into an integer using ".to_i" on your num variable.
Secondly, this code puts you into an infinite loop since you are using a "while" loop.
Since the number is only input once, you get stuck in the "while" loop forever no matter what the input is. Basically, "num" never stops being not 0.
You'd be better off using an if..else statement. Something like:
num = gets.chomp.to_i
if num != 0
if (num%2) == 0
puts 'yess'
else
puts 'nooo'
end
else
puts "that's 0, dude"
end
Integers have two methods for this. They are even? and odd?.
You can use this in your if statement as so:
if num.even?
puts 'yess'
else
puts 'nooo'
end
However, an easier way to write this is with ternary expressions:
puts num.even? ? "yes" : "no"
However, make sure num is an Integer. Anything coming from gets will be a String. So, you should be doing num = gets.chomp.to_i. Anything that is not a number, like "h", will return 0.
"5".to_i #=> 5
"h".to_i #=> 0
Hey I was given the fizzbuzz task recently and I had answered with the usual,
if ((i%3==0) || (i.to_s.include?('3'))) && ((i%7==0) || (i.to_s.include?('7')))
p 'Fizzbuzz'
elsif (i%3==0) || (i.to_s.include?('3'))
p 'Fizz'
elsif (i%7==0) || (i.to_s.include?('7'))
p 'Buzz'
else
p i
end
and when asked to shorten it I tried using ternary operators:
p (i%3<1 || i.to_s.include?('3')) ? ((i%7<1 || i.to_s.include?('7')) ? "Fizzbuzz" : "Fizz") : ((i%7<1 || i.to_s.include?('7')) ? "Buzz" : i)
but when asked to solve it using the Enumerable methods(select,reject,collect etc) I was well stumped...Any body tried this before??
The select/collect methods were specificaly mentioned so I'm guessing that he had something like this in mind(excuse the crappy code)
(1..100).select { |i| i % 3 == 0 }.collect { "fizz" }
but i'm stuck when trying to do this for the 3 conditions and print out the result(ie iterate through the output array) :\
Probably not much help now, but I've produced a gem (fizzbuzzard) that monkey patches Fixnum such that all multiples of three print as Fizz, all multiples of five etc etc. Interviewers will respect you for using your knowledge of existing libraries rather than pointlessly re-solving solved problems.
Get it from rubgems here and find the source here.
The part I'm proudest of? The test suite. Output reproduced here:
(0h8m|master) [fizzbuzzard] $ rspec
.
Finished in 0 seconds
1 example, FIZZBUZZ failures
def fizzbuzz(i)
[nil,nil,"Fizz",nil, nil,nil,"Buzz"].each_with_index do |fizz_or_buzz, idx|
print fizz_or_buzz if (i.to_i % (1+idx)).zero?
end
end
If the interviewer doesn't laugh, then it's probably not going to be a good fit (for me anyways).
Note that print doesn't add a newline (vs p)
See http://rosettacode.org/wiki/FizzBuzz#Ruby for more common solutions.
This one's a little dense but fun. It abuses arrays and some functional methods slightly to avoid using any if statements.
rules = {
3 => ["fizz"],
7 => ["buzz"]
}
(1..100).map do |n|
[rules.map do |key, value|
value[n % key]
end.compact.join, n.to_s].find do |word|
!word.empty?
end
end.join("\n")
It is not clean to repeat the same conditionals.
p case [
i.%(3).zero? || i.to_s.include?("3"),
i.%(7).zero? || i.to_s.include?("7")
]
when [true, true] then "Fizzbuzz"
when [true, false] then "Fizz"
when [false, true] then "Buzz"
when [false, false] then i
end
This hands you back the results in an array of strings, you can do whatever you want with them at that point.
results = (1..100).to_a.collect do |i|
f = ((i%3).zero? || i.to_s.include?('3'))
b = ((i%7).zero? || i.to_s.include?('7'))
"#{:fizz if f}#{:buzz if b}#{i unless f || b}"
end
p results