Iterate every two elements in ruby for loop - ruby

How do you create a for loop like
for (int x=0; x<data.length; x+=2)
in ruby? I want to iterate through an array but have my counter increment by two instead of one.

If what you really want is to consume 2 items from an array at a time, check out each_slice.
[1,2,3,4,5,6,7,8,9].each_slice(2) do |a, b|
puts "#{a}, #{b}"
end
# result
1, 2
3, 4
5, 6
7, 8
9,

Ruby's step is your friend:
0.step(data.length, 2).to_a
=> [0, 2, 4, 6]
I'm using to_a to show what values this would return. In real life step is an enumerator, so we'd use it like:
data = [0, 1, 2, 3, 4, 5]
0.step(data.length, 2).each do |i|
puts data[i]
end
Which outputs:
0
2
4
<== a nil
Notice that data contains six elements, so data.length returns 6, but an array is a zero-offset, so the last element would be element #5. We only get three values, plus a nil which would display as an empty line when printed, which would be element #6:
data[6]
=> nil
That's why we don't usually walk arrays and container using outside iterators in Ruby; It's too easy to fall off the end. Instead, use each and similar constructs, which always do the right thing.
To continue to use step and deal with the zero-offset for arrays and containers, you could use:
0.step(data.length - 1, 2)
but I'd still try working with each and other array iterators as a first choice, which #SergioTulentsev was giving as an example.

(0..data.length).step(2) do |x|
puts x
end
This seems like the closest substitute.

Using Range#step:
a = (1..50).to_a
(1..a.size).step(2).map{|i| a[i-1]} # [1, 3, 5, 7, 9 ...

Related

Why is my filter method not removing some elements that should be removed?

I am trying to create a method called filer_out! that takes in an array and a proc, and returns the same array but with every element that returns true when it is run through the proc, with the caveat being we can't use Array#reject!
I wrote this:
def filter_out!(array, &prc)
array.each { |el| array.delete(el) if prc.call(el)}
end
arr_2 = [1, 7, 3, 5 ]
filter_out!(arr_2) { |x| x.odd? }
p arr_2
but when I run the code, what prints out is:
[7, 5]
even though the answer should be:
[]
Upon review of the solution I see Array#uniq was used first:
def filter_out!(array, &prc)
array.uniq.each { |el| array.delete(el) if prc.call(el) }
end
arr_2 = [1, 7, 3, 5 ]
filter_out!(arr_2) { |x| x.odd? }
p arr_2
and the correct output was displayed:
[]
So I guess what my question is, why do you have to use Array#uniq in order to get the correct solution?
thanks for your help!
The problem here is the method delete modify the original array. Here the deal if you put some information out:
def filter_out!(array, &prc)
array.each.with_index do |el, i|
p "Current index #{i}"
p "Current array #{array}"
p "Current element #{el}"
array.delete(el) if prc.call(el)
end
end
arr_2 = [1, 7, 3, 5 ]
filter_out!(arr_2) { |x| x.odd? }
# Output:
#"Current index 0"
# "Current array [1, 7, 3, 5]"
# "Current element 1"
# "Current index 1"
# "Current array [7, 3, 5]"
# "Current element 3"
Explain:
The first time, the element is 1, after it's deleted the array is [7, 3, 5]
The second time, index in the iteration is 1, it gets the current element with this index in the current array, in this case, is 3 not 7 and delete it, after deleting the array is [3, 5]
After two times it stops iteration because the current index is out of range of the current array
By using uniq you get the right result because array.uniq it creates a copy of the original array when the original array is modified, it still iteration as expect.
Traversing an array uses an internal "cursor" of some kind that points to the current element, e.g.:
[ 1, 7, 3, 5 ] # 1st step
# ^
[ 1, 7, 3, 5 ] # 2nd step (increment cursor)
# ^
# etc.
If you remove the current element during iteration, the cursor immediately points to the next element, thus skiping an element when it gets incremented on the next turn:
[ 1, 7, 3, 5 ] # 1st step
# ^
[ 7, 3, 5 ] # 1nd step (remove element under cursor)
# ^
[ 7, 3, 5 ] # 2nd step (increment cursor)
# ^
[ 7, 5 ] # 2nd step (remove element under cursor)
# ^
# done
A typical work-around is to iterate the array in reverse order, i.e.:
[ 1, 7, 3, 5 ] # 1st step
# ^
[ 1, 7, 3 ] # 1nd step (remove element under cursor)
# ^
[ 1, 7, 3 ] # 2nd step (decrement cursor)
# ^
[ 1, 7 ] # 2nd step (remove element under cursor)
# ^
# etc.
Note that the cursor might be out of bounds in this algorithm, so you have to be careful in that regard.
The above as Ruby code:
def filter_out!(array)
(array.size - 1).downto(0) do |i|
array.delete_at(i) if yield array[i]
end
end
As a general rule, not just for this question, and not just for Ruby, it is never a good idea to mutate a collection while you are iterating over it. Just don't do that. Ever.
Actually, personally, I just think you should avoid any mutation at all, as far as feasible and sensible, but that might be a tad extreme.
Your code is also committing another cardinal sin: never mutate an argument. Ever. You should only use arguments to compute the result, you should never mutate them. That is hugely surprising to anyone who calls your method, and in programming, surprises are dangerous. They lead to bugs and security holes.
Lastly, you are using the bang ! naming convention wrong. The bang is used to mark the "more surprising" of a pair of methods. You should only have a filter_out! method if you also have a filter_out method. And speaking of style, indentation in Ruby is 2 spaces, not 4, as per standard community coding style.
Okay, having said that, let's look at what happens in your code.
Here is the relevant part of the implementation of Array#each from the Rubinius Ruby implementation, defined in core/array.rb#L62-L77:
def each
i = #start
total = i + #total
tuple = #tuple
while i < total
yield tuple.at(i)
i += 1
end
end
As you can see, it is just a simple while loop incrementing the index every time. Other Ruby implementations are similar, e.g. here is a simplified version of JRuby's implementation in core/src/main/java/org/jruby/RubyArray.java#L1805-L1818:
public IRubyObject each(ThreadContext context, Block block) {
for (int i = 0; i < size(); i++) {
block.yield(context, eltOk(i));
}
}
Again, just a simple index loop.
In your case, we are starting with an array, whose backing store looks like this:
1 7 3 5
On the first iteration of the each, the iteration counter is at index 0:
1 7 3 5
↑
1 is odd, so we delete it, and the situation now looks like this:
7 3 5
↑
The last thing we do in the loop iteration is to increase the iteration counter by one:
7 3 5
↑
Okay, in the next iteration, we check again: 3 is odd, so we delete it:
7 5
↑
We increase the iteration counter:
7 5
↑
And now we have the exit condition of our loop: i is no longer less than the size of the array: i is 2 and the size is also 2.
Note that in the JRuby implementation, the size is checked with a call to the size() method, which means it is re-calculated every time. In Rubinius, however, the size is cached, and only calculated once before the loop starts. Therefore, Rubinius will actually try to keep going here and access the third element of the backing tuple which doesn't exist, which leads to this NoMethodError exception:
NoMethodError: undefined method `odd?' on nil:NilClass.
Accessing a non-existing element of a Rubinius::Tuple returns nil, and each is then passing that nil to the block, which tries to call Integer#odd?.
It is important to note that Rubinius is doing nothing wrong here. The fact that it raises an exception is not a bug in Rubinius. The bug is in your code: mutating a collection while iterating over it is simply not allowed.
All of this now explains why the solution which calls Array#uniq first works: Array#uniq returns a new array, so the array you are iterating over (the one returned returned from Array#uniq) and the array you are mutating (the one referenced by the array parameter binding) are two different arrays. You would have gotten the same result with, e.g. Object#clone, Object#dup, Enumerable#map, or many others. Just as an example:
def filter_out(array)
array.map(&:itself).each { |el| array.delete(el) if yield el }
end
arr_2 = [1, 7, 3, 5]
filter_out(arr_2, &:odd?)
p arr_2
However, a more idiomatic Ruby solution would be something like this:
def filter_out(array, &blk)
array.delete_if(&blk)
end
arr_2 = [1, 7, 3, 5]
arr_3 = filter_out(arr_2, &:odd?)
p arr_3
This fixes all the problems with your code:
It doesn't mutate the array.
It doesn't mutate any argument.
In fact, there is no mutation going on at all.
It uses existing methods from the Ruby core library instead of re-inventing the wheel.
No bang !
Two spaces indentation.
The only real question is whether the method is needed at all, or if it would make more sense to just write
arr_2 = [1, 7, 3, 5]
arr_3 = arr_2.delete_if(&:odd?)
p arr_3

How to find the sum of each row for a multi-dimensional array

I would like to find the sum of each row of a multidimensional array, and have the sums an array, e.g., for [[1,2,3],[1,1,1]], I would like to get [6,3].
I tried the following:
arr = [[1,2,3],[3,2,1],[2,1,3]]
print arr.each{|row| row.each{|column| puts column}}
Results:
1
2
3
3
2
1
2
1
3
[[1, 2, 3], [3, 2, 1], [2, 1, 3]]
I am struggling with it. I still don't fully understand each iterators. Any help would be appreciated.
For ruby 2.4.0 or newer
a.map { |suba| suba.sum }
or simply
a.map(&:sum)
for ruby prior to 2.4.0
a.map { |suba| suba.inject(0, :+) }
[[1,2,3],[1,1,1]].map{|a| a.inject(:+)} # => [6, 3]
If there is a possibility that any of the sub-array can be empty, then you need to add the initial 0, as Ursus pointed out.
[[1,2,3],[1,1,1]].map{|a| a.inject(0, :+)} # => [6, 3]
[[1,2,3],[1,1,1]].map { |a| a.inject(0, :+) } # => [6 , 3]
map changes each element to the return value of this elements block
get the sum for each array with inject(:+)
use inject(0, :+) to set 0 instead of the first element as the start value (handles empty inner array)
see:
Enumerable#inject
Enumerable#map
"How to find the sum of each row"
arr = [[1,2,3], [1,1,1]]
print arr.each{|row| <-- here you have each row
So now row contains [1,2,3] initially. As others have mentioned, you can apply a sum here. (You don't need the leading print).
arr.each{|row| puts row.sum}
Result:
6
3
But a better way to do it is with map. As I told a Ruby newbie many years ago, think of map when you want to change every element to something else, in other words a "1:1 mapping" of input to output. In this case the output is row.sum:
sums = arr.map{|row| row.sum}
Result:
[6, 3]

Adding the number to the according index it is positioned in

def incrementer(num)
num.map.with_index(1) do |row, index|
if row > 9
row.to_s.reverse.chop.to_i
else
index + row
end
end
end
Hi,
I have a method which adds the number to the according index it is positioned in. However I have two rules:
The index starts at (1)
If the number is a multiple, remove the first integer off the end of it. 12 would become 2 for example.
The problem is I am unsure how to include an 'if' statement inside a block and I believe I am doing it wrong. I know there is a much better way to write this statement but I am finding it hard.
Example:
incrementer([4,6,9,1,3]) => [5,8,2,5,8]
- 9 + 3 (position of 9 in array) = 12
- Only its last digit 2 should be returned
Fixing your code
Use map followed by with_index(1), the argument offsetting the initial index by +1.
def incrementer(num)
num.map.with_index(1) do |row, index|
row = index + row
if row > 9
row.digits.first
else
row
end
end
end
incrementer [3, 2, 4, 10] #=> [4, 4, 7, 4]
incrementer [4, 6, 9, 1, 3] #=> [5, 8, 2, 5, 8]
Negative numbers
Numbers can be negative and for which we can use abs.digits but it may be better to use Integer#remainder.
def incrementer(num)
num.map.with_index(1) do |row, index|
row = index + row
case row
when -9..9
row
else
row.remainder 10
end
end
end
incrementer [3, 2, 4, 10]
#=> [4, 4, 7, 4]
incrementer [4, 6, 9, 1, 3]
#=> [5, 8, 2, 5,8]
incrementer [3, 2, 4,-17]
#=> [4, 4, 7, -3]
incrementer [4, 6, -22, 1, 3]
#=> [5, 8, -9, 5, 8]
Why not use % instead of Integer#remainder?
a % b returns a modulo b which is not the same thing as the remainder. This has consequences for negative numbers:
-12 % 10 #=> 8 not the last digit
So we need to use Integer#remainder, so:
-12.remainder 10 #=> -2
Which is what we need, i.e. the last digit (parity included). It can be noted in other languages such as JavaScript, a % b returns the remainder.
As noted by #moveson the above code can be refactored to:
num.map.with_index(1) { |row, index| (index + row).remainder 10 }
The #digits method requires Rails or ActiveSupport (or Ruby 2.4+), and it's not necessary. Here is a pure Ruby solution that works with any Ruby version:
def incrementer(array)
array.map.with_index(1) { |integer, index| (integer + index) % 10 }
end
This code above says: For each element of the array, add its index (starting from 1), divide the sum by 10, and return the remainder.
The % (modulo) operator divides the number before it by the number after it and returns the remainder. For example, 22 % 7 returns 1. It's an extremely useful tool and can often help avoid the use of conditionals entirely, such as in your example. Using %, you can get the last digit of a number n (whether or not n is greater than 9) by simply taking n % 10.
Now you can do:
>> incrementer([3,2,4,10])
=> [4, 4, 7, 4]
>> incrementer([4,6,9,1,3])
=> [5, 8, 2, 5, 8]
You've got two separate problems. The first problem is your use of each_with_index. Ruby functions return the value of the last expression they execute, so if you look at your function:
def incrementer(num)
num.each_with_index do |row, index|
# do some stuff
end
end
It essentially calls each_with_index and returns the value. The issue here is that each_with_index iterates over an array and then returns the original array. What you want to do is change each item in the array and return a new array with the new values. For this, you can use map:
def incrementer(num)
num.map.with_index(1) do |row, index|
# do some stuff
end
end
In this case, you can even conveniently pass in the parameter 1 to tell the iterator to start at index 1.
Now the second problem is that your if-else-statement either iterates a number or wraps it around. But what you actually want to do is iterate a number and wrap it around if it's bigger than 9. You can do that like so:
def incrementer(num)
num.map.with_index(1) do |row, index|
row = index + row
if row > 9
row.to_s.reverse.chop.to_i
else
row
end
end
end
What we're doing here is iterating the number first, saving it into row and then checking to see if it's over 9. If it is, we return the last digit; otherwise, we just return the value row.
This code will do what you want, but let's make one more change for the sake of clarity. This code:
row.to_s.reverse.chop.to_i
Is a little bit confusing. Ruby 2.4 has a convenient method for getting at the digits of an integer:
row.digits.first
This is easy to read, but it's a bit slow since it turns an integer into an array of integers. A better way is to use modulus % to get the remainder of your number, divided by 10. This is fast and easy to read for most programmers, since it's a common trick.
row % 10
Putting it all together, you get:
def incrementer(num)
num.map.with_index(1) do |row, index|
row = index + row
if row > 9
row % 10
else
row
end
end
end
Special thanks to (#sagarpandya84) and (#moveson) for allowing me to build on their answers.

How to refactor this code to remove output variable?

def peel array
output = []
while ! array.empty? do
output << array.shift
mutate! array
end
output.flatten
end
I have not included the mutate! method, because I am only interested in removing the output variable. The mutate! call is important because we cannot iterate over the array using each because array is changing.
EDIT: I am getting an array as output, which is what I want. The method works correctly, but I think there is a way to collect the array.shift values without using a temp variable.
EDIT #2: OK, here is the mutate! method and test case:
def mutate! array
array.reverse!
end
a = (1..5).to_a
peel( a ).should == [ 1, 5, 2, 4, 3 ]
It doesn't matter if peel modifies array. I guess it should be called peel!. Yes, mutate! must be called after each element is removed.
All this reversing makes me dizzy.
def peel(array)
indices = array.size.times.map do |i|
i = -i if i.odd?
i = i/2
end
array.values_at(*indices) # indices will be [0, -1, 1, -2, 2] in the example
end
a = (1..5).to_a
p peel(a) #=>[1, 5, 2, 4, 3]
Another approach:
def peel(array)
mid = array.size/2
array[0..mid]
.zip(array[mid..-1].reverse)
.flatten(1)
.take(array.size)
end
Usage:
peel [1,2,3,4,5,6]
#=> [1, 6, 2, 5, 3, 4]
peel [1,2,3,4,5]
#=> [1, 5, 2, 4, 3]
Here's a way using parallel assignment:
def peel array
n = array.size
n.times {|i| (n-2-2*i).times {|j| array[n-1-j], array[n-2-j] = array[n-2-j], array[n-1-j]}}
array
end
peel [1,2,3,4,5] # => [1,5,2,4,3]
peel [1,2,3,4,5,6] # => [1,6,2,5,3,4]
What I'm doing here is a series of pairwise exchanges. By way of example, for [1,2,3,4,5,6], the first 6-2=4 steps (6 being the size of the array) alter the array as follows:
[1,2,3,4,6,5]
[1,2,3,6,4,5]
[1,2,6,3,4,5]
[1,6,2,3,4,5]
The 1, 6 and the 2 are in now the right positions. We repeat these steps, but this time only 6-4=2 times, to move the 5 and 3 into the correct positions:
[1,6,2,3,5,4]
[1,6,2,5,3,4]
The 4 is pushed to the end, it's correct position, so we are finished.

Enumerators in Ruby

I'm having a trouble understanding Enumerators in Ruby.
Please correct me If I'm wrong, o.enum_for(:arg) method is supposed to convert object to Enumerator and every iteration over object o should call arg method?
What confuses me is how this line of code works
[4, 1, 2, 0].enum_for(:count).each_with_index do |elem, index|
elem == index
end
It should count how many elements are equal to their position in the array, and it works. However, I don't understand what's actually going on. Is each_with_index calling count method on every iteration? If someone could explain, it would be great.
From Programming Ruby:
count enum.count {| obj | block } → int
Returns the count of objects in enum that equal obj or for which the
block returns a true value.
So the enumerator visits each element and adds 1 to the count if the block returns true, which in this case means if the element is equal to the array index.
I haven't seen this pattern used much (if at all)—I would be more inclined to:
[4, 1, 2, 0].each_with_index.select { |elem, index| elem == index }.count
EDIT
Lets take a look at the example from your comment:
[4, 1, 2, 0].enum_for(:each_slice, 2).map do |a, b|
a + b
end
each_slice(2) takes the array 2 elements at a time and returns an array for each slice:
[4, 1, 2, 0].each_slice(2).map # => [[4,1], [2,0]]
calling map on the result lets us operate on each sub-array, passing it into a block:
[4, 1, 2, 0].enum_for(:each_slice, 2).map do |a,b|
puts "#{a.inspect} #{b.inspect}"
end
results in
4 1
2 0
a and b get their values by virtue of the block arguments being "splatted":
a, b = *[4, 1]
a # => 4
b # => 1
You could also take the array slice as the argument instead:
[4, 1, 2, 0].enum_for(:each_slice, 2).map {|a| puts "#{a.inspect}"}
[4, 1]
[2, 0]
Which lets you do this:
[4, 1, 2, 0].enum_for(:each_slice, 2).map {|a| a.inject(:+) } #=> [5,2]
Or if you have ActiveSupport (i.e. a Rails app),
[4, 1, 2, 0].enum_for(:each_slice, 2).map {|a| a.sum }
Which seems a lot clearer to me than the original example.
array.count can normally take a block, but on its own, it returns a fixnum, so it can't be chained to .with_index the way some other iterators can (try array.map.with_index {|x,i ... }, etc).
.enum_for(:count) converts it into a enumerator, which allows that chaining to take place. It iterates once over the members of array, and keeps a tally of how many of them equal their indexes. So count is really only being called once, but only after converting the array into something more flexible.

Resources