Why will a Range not work when descending? [duplicate] - ruby

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.

Related

How do print odd numbers using block { }

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

Iterating over each element of an array, except the first one

What is the idiomatic Ruby way to write this code?
Given an array, I would like to iterate through each element of that array, but skip the first one. I want to do this without allocating a new array.
Here are two ways I've come up with, but neither feels particularly elegant.
This works but seems way too verbose:
arr.each_with_index do |elem, i|
next if i.zero? # skip the first
...
end
This works but allocates a new array:
arr[1..-1].each { ... }
Edit/clarification: I'd like to avoid allocating a second array. Originally I said I wanted to avoid "copying" the array, which was confusing.
Using the internal enumerator is certainly more intuitive, and you can do this fairly elegantly like so:
class Array
def each_after(n)
each_with_index do |elem, i|
yield elem if i >= n
end
end
end
And now:
arr.each_after(1) do |elem|
...
end
I want to do this without creating a copy of the array.
1) Internal iterator:
arr = [1, 2, 3]
start_index = 1
(start_index...arr.size).each do |i|
puts arr[i]
end
--output:--
2
3
2) External iterator:
arr = [1, 2, 3]
e = arr.each
e.next
loop do
puts e.next
end
--output:--
2
3
OK, maybe this is bad form to answer my own question. But I've been racking my brain on this and poring over the Enumerable docs, and I think I've found a good solution:
arr.lazy.drop(1).each { ... }
Here's proof that it works :-)
>> [1,2,3].lazy.drop(1).each { |e| puts e }
2
3
Concise: yes. Idiomatic Ruby… maybe? What do you think?

Ruby's "each" methods not iterating over all items in an array?

I'm trying out the following code:
a = [1,2,3,4]
a.each do
puts "Removing #{a.last}"
a.pop
end
but instead of getting all four numbers popped I only get the first 3. Indeed, doing something like puts a.length returns 1 and puts-ing it shows the element "1" is still there.
How do I need to use the method correctly?
(I'm using Ruby 2.0).
I suspect this is happening because you're iterating over the elements of the list while modifying the list.
Try the following:
a = [1,2,3,4]
until a.empty? do
puts "Removing #{a.last}"
a.pop
end
Problem
While you are iterating over a you are changing it.
Explanation of Problem
That means once you have removed an element the each method gets thrown off because suddenly the number of elements a contains is one less. And thus indexing is also thrown off.
If I just execute this:
a = [1,2,3,4]
a.each do
|thing|
puts thing
a.delete(thing)
end
I will get the output [1,3].
That is because the following happens:
Before I remove the 1 from the list that is at index 0, the 2 is at index 1.
After the 1 is removed the 2 is at index 0 instead of 1 so not the 2 is the next element that is iterated over but the 3!
By the way you can define a local block variable like I did with thing to access each element that you iterate over.
Solution
In order to get what you want you need to create a copy and work on that.
a = [1,2,3,4]
b = a.clone
a.each do
|thing|
puts thing
b.delete(thing)
end
Now a remains the same while you iterate over it and you change b instead.
So at the end of this loop a = [1,2,3,4] and b =[].
After you said a = b you will have the desired result.
Of Course you can adapt this for popping elements from the back. Just make sure to work on a copy so you do not change the element while you are iteration over it.
Some other answers tell why your code does not work.
An alternative way to do it would be like this (provided that you do not have nil or false in a):
a = [1,2,3,4]
while e = a.pop
puts "Removing #{e}"
end
a = [1,2,3,4]
a.length.times do
puts "Removing #{a.last}"
a.pop
end
Look at the output,which makes clear why it seems to you each not getting run with all the array elements. As you are pop(ing)Array#pop, so the elements from the last is deleted. When each passes 2 to the block,then original array a is got empty,so each stops the iteration.:
a = [1,2,3,4]
a.each do |i|
puts i
puts "Removing #{a.last}"
p a.pop
p "========"
end
Output:
1
Removing 4
4
"========"
2
Removing 3
3
"========"
So you can use the below:
a = [1,2,3,4]
(0...a.size).each do |i|
p a.pop
end
Output:
4
3
2
1
Try this:
a.count.times do a.pop and puts "Removing #{a.last + 1 rescue 1}" end
Should do the same in a do loop

last element in Hash.each [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Tell the end of a .each loop in ruby
I have a Hash:
=> {"foo"=>1, "bar"=>2, "abc"=>3}
and a code:
foo.each do |elem|
# smth
end
How to recognize that an element in cycle is last?
Something like
if elem == foo.last
puts 'this is a last element!'
end
For example like this:
foo.each_with_index do |elem, index|
if index == foo.length - 1
puts 'this is a last element!'
else
# smth
end
end
The problem you might have is that items in a map are not coming in any specific order. On my version of Ruby I see them in the following order:
["abc", 3]
["foo", 1]
["bar", 2]
Maybe you want to traverse the sorted keys instead. Like this for example:
foo.keys.sort.each_with_index do |key, index|
if index == foo.length - 1
puts 'this is a last element!'
else
p foo[key]
end
end

Is there an each_if in ruby?

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 }

Resources