Let's say I have this each loop in Ruby.
#list.each { |i|
puts i
if i > 10
break
end
}
Where I want to loop through the list until a condition is met.
This stung me as "un Ruby'ish" and since I'm new to Ruby, is there a Ruby way to do this?
You can use Enumerable#detect or Enumerable#take_while, depending on the result you want.
#list.detect { |i|
puts i
i > 10
} # Returns the first element greater than 10, or nil.
As others have noted, a better style would be to first make your sub-selection and then act on it, e.g.:
#list.take_while{ |i| i <= 10 }.each{|i| puts i}
You could use take_while:
#list.take_while { |i| i <= 11 }.each { |i| puts i }
Related
I am trying to print my array with just odd numbers using the block method but i am not too sure how to.
I can print odd numbers using no block but do but do not know how to implement it into the block method { }
#non block method
array = [1,2,3,4,5,6,7,8]
array.each do |i|
if i % 2 == 0
puts "#{i}"
end
end
#output of 2 4 6 8
#block method not sure how
array = [1,2,3,4,5,6,7,8]
array.each {|i| put i if i % 2 == 0 end }
#expected output should be 2 4 6 8
Thank you in advanced !
your block is almost correct you just need to remove the end as it's an inline (or trailing) if method, you also need to use puts and not put
array.each {|i| puts i if i % 2 == 0 }
also, note that ruby has a .even? and .odd? methods you can call on integers
array.each {|i| puts i if i.odd? }
Another option is to select the even? elements and print them afterwards:
array.select(&:even?).each { |i| puts i }
Or alternatively via reject and odd?:
array.reject(&:odd?).each { |i| puts i }
The each call isn't really needed, as you can pass an entire array to puts and it will print each element on a separate line:
puts array.select(&:even?)
# or
puts array.reject(&:odd?)
All of the above will generate the same output:
2
4
6
8
I'm trying to dynamically generate a case statement based on an array of values. For example let's say I have an array of ranges
[1..3,4..6,7..20,21..38]
and I want to write a dynamic case statement that returns the first number of whatever range
case n
ranges.each do |r|
when r
r.first
end
end
Is this possible, or will I have to find another way to do it (my actual code is more complex)?
If i get your question right, then you can forget case statement and do it using detect:
ary = [1..3, 4..6, 7..20, 21..38]
num = 15 # say
ary.detect { |sub_ary| sub_ary.include?(num) }
=> 7..20
ary.detect { |sub_ary| sub_ary.include?(num) }.first # call `first` on result of above, which is a range, to get the first element.
=> 7
Just out of curiosity:
number = 5
instance_eval [
"case number",
*ranges.map { |r| "when #{r} then (#{r}).first" },
"end"
].join($/)
#⇒ 4
In addition to #detect (or #find) with #include? from Jagdeep Singhs answer you can also use the case equality operator (Range#===). This operator is used by the case statement to compare the input value with the scenario's you're providing.
ranges.find { |range| range === n }.first
Keep in mind both #detect and #find return nil if no value can be found. This means you might want to use the safe navigation operator (}&.first) to prevent a no method exception of #first on nil if the value can't be found.
Well, this works, but is kind of pointless and thread unsafe:
def get_range(n)
ranges = [1..3,4..6,7..20,21..38]
case n
when 3
# special case
199
when ->(x) { #_get_range = ranges.find { |r| r.cover?(x) } }
#_get_range.first
else
0
end
ensure
remove_instance_variable(:#_get_range) if instance_variable_defined?(:#_get_range)
end
get_range(3) # => 199
get_range(5) # => 4
get_range(50) # => 0
You could just do:
ranges.find { |r| r.cover?(n) }&.first || 0
My two cents..
ranges = [1..3,4..6,7..20,21..38]
num = 15
ranges.bsearch { |range| range.member? num }.begin
How can I shorten this expression?
if artist != 'Beck' && artist != 'Led Zeppelin'
5.times { puts 'sorry' }
end
Is there a shorthand check for two logical conditionals against one variable?
As an aside, this turned into
class String
def is_not?(*arr)
!arr.include?(self)
end
end
In our project.
Now we can do 'foo'.is_not?('bar', 'batz')
unless ['Beck', 'Led Zeppelin'].include?(artist)
5.times { puts 'sorry' }
end
Isn't any "shorter", but no obscure syntax trickery too. Just using regular array api. As a consequence, you can provide that array in any way you want. Load it from a file, for example. With any number of elements.
Your specific case is pretty minimal, but if you have lots of unrelated conditions to test for lots of values you can set the tests up as lambdas in an array and use all?. For instance, the following example filters all the integers between 1 and 100 for those which are > 20, < 50, even, and divisible by 3:
tests = [
->(x) { x > 20 },
->(x) { x < 50 },
->(x) { x.even? },
->(x) { x % 3 == 0 }
]
(1..100).each do |i|
puts i if tests.all? { |test| test[i] }
end
case artist
when 'Beck', 'Led Zeppelin'
else
5.times { puts 'sorry' }
end
To set i and its maximum and have a double iterator In Java, I would use for loops as follows:
for (i=1; i<10, i+=){
for (j=1; j<10; j++){
puts i (or whatever function)
}
}
I'm trying to figure out how to do this in Ruby. The for loops I've seen in Ruby are for ranges, and single iterator:
for i in (1..10)
puts i
end
or while loops
i = 1
while i < 10
puts i
i += 1
end
Are these while loops the (single iterator) Ruby equivalent of the Java I mentioned, or is there another way to do these for loops?
The "ruby-ish" way would be each
(1...10).each do |i|
puts i
end
or to double iterate
(1...10).each do
(1...10).each do |j|
puts j
end
end
As #maxwilliams and #sawa point out, (1...10) is 1 through 9 inclusive, (1..10) is 1 through 10 inclusive.
Why not do a nested loop?
for i in 1...10
for j in 1...10
puts i
end
end
By the way, the Ruby equivalent is for i in 1...10, not for i in 1..10.
In Ruby you rarely use the for loop. Actually, you never use it. In Ruby you use enumerators and blocks.
for (i=1; i<10, i+=){
for (j=1; j<10; j++){
puts i (or whatever function)
}
}
is equivalent to
(1...10).each do |i|
(1...10).each do |j|
puts j
end
end
Also note there is a difference between
(1..10).each do |i|
end
and
(1...10).each do |i|
end
The latter is equivalent to
(1..9).each do |i|
end
In fact, the .. will create an inclusive range whereas ... an exclusive range.
It is also worth to mention that in Ruby you rarely use indexes to loop items. Therefore, if your goal is to print out some elements in an array, your code should not be
(1..10).each do |i|
p #items[i]
end
rather you call each on the collection.
#items.each do |item|
p item
end
In certain cases, like in your question, you don't even need a range (when your loop starts from 1. You can use times.
9.times do |i|
9.times do |j|
puts j
end
end
In Ruby we don't use parenthesis. They are uncool.
This is an example for while statement in Ruby
while $i < $num do
puts "Value for i is #{i}"
$i +=1
end
And this is an example for for loop.
for i in 0..10
puts "Value of local variable is #{i}"
end
But there are more loops! Check this for more info http://www.tutorialspoint.com/ruby/ruby_loops.htm
But if you are Java programmer, you can check this link https://www.ruby-lang.org/en/documentation/ruby-from-other-languages/to-ruby-from-java/
This question already has answers here:
Is there a reason that we cannot iterate on "reverse Range" in ruby?
(12 answers)
Closed 7 years ago.
Why will (1..5).each iterate over 1,2,3,4,5, but (5..1) will not? It returns the Range instead.
1.9.2p290 :007 > (1..5).each do |i| puts i end
1
2
3
4
5
=> 1..5
1.9.2p290 :008 > (5..1).each do |i| puts i end
=> 5..1
The easiest way to do that is use downto
5.downto(1) do |i| puts i end
Ranges use <=> to determine if an iteration is over; 5 <=> 1 == 1 (greater-than), so it's done before it starts. Even if they didn't, ranges iterate using succ; 5.succ is 6, still out of luck. A range's step cannot be negative, so that won't work either.
It returns the range because each returns what it was called on. Use downto if it's the functionality itself you're looking for, otherwise the above answers your actual question regarding "why".
You can easily extend the Range class, in particular the each method, to make it compatible with both ascending and descending ranges:
class Range
def each
if self.first < self.last
self.to_s=~(/\.\.\./) ? last = self.last-1 : last = self.last
self.first.upto(last) { |i| yield i}
else
self.to_s=~(/\.\.\./) ? last = self.last+1 : last = self.last
self.first.downto(last) { |i| yield i }
end
end
end
Then, the following code will perform just as you'd expect:
(0..10).each { |i| puts i}
(0...10).each { |i| puts i}
(10..0).each { |i| puts i}
(10...0).each { |i| puts i}
This doesn't even really have anything to do with Ruby, it's just simple basic math: the range which starts with 5 and ends with 1 is empty. There is nothing to iterate over.
Because Ruby only does what it's told, not what you mean.
It can't tell whether you want to go in reverse (ie 5, 4, 3, 2, 1), or whether you really only want the numbers starting from 5 that are less than or equal to 1. It's theoretically possible that someone may want the latter, and because Ruby can't tell what you really want, it'll go with the latter.