Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The most idiomatic way to iterate through a Ruby array, exiting when an arbitrary condition met?

I want to iterate through an array, each element of which is an array of two integers (e.g. `[3,5]'); for each of these elements, I want to calculate the sum of the two integers, exiting the loop when any of these sums exceeds a certain arbitrary value. The source array is quite large, and I will likely find the desired value near the beginning, so looping through all of the unneeded elements is not a good option.

I have written three loops to do this, all of which produce the desired result. My question is: which is more idiomatic Ruby? Or--better yet--is there a better way? I try not to use non-local loop variables in, but break statements look kind of hackish to my (admittedly novice) eye.

# Loop A
pairs.each do |pair|
  pair_sum = pair.inject(:+) 
  arr1 << pair_sum
  break if pair_sum > arr2.max
end

#Loop B - (just A condensed)
pairs.each { |pair| arr1.last <= arr2.max ? arr1 << pair.inject(:+) : break }

#Loop C
i = 0
pair_sum = 0
begin
  pair_sum = pairs[i].inject(:+)
  arr1 << pair_sum
  i += 1
end until pair_sum > arr2.max

A similar question was asked at escaping the .each { } iteration early in Ruby, but the responses were essentially that, while using .each or .each_with_index and exiting with break when the target index was reached would work, .take(num_elements).each is more idiomatic. In my situation, however, I don't know in advance how many elements I'll have to iterate through, presenting me with what appears to be a boundary case.

This is from a project Euler-type problem I've already solved, btw. Just wondering about the community-preferred syntax. Thanks in advance for your valuable time.

like image 311
jdburns Avatar asked Apr 30 '13 08:04

jdburns


People also ask

Do you know the best way to iterate through the items of an array Ruby?

The Ruby Enumerable#each method is the most simplistic and popular way to iterate individual items in an array. It accepts two arguments: the first being an enumerable list, and the second being a block. It takes each element in the provided list and executes the block, taking the current item as a parameter.

What does the .each method do in Ruby?

The each() is an inbuilt method in Ruby iterates over every element in the range. Parameters: The function accepts a block which specifies the way in which the elements are iterated. Return Value: It returns every elements in the range.

How do you end a loop in Ruby?

In Ruby, we use a break statement to break the execution of the loop in the program. It is mostly used in while loop, where value is printed till the condition, is true, then break statement terminates the loop. In examples, break statement used with if statement. By using break statement the execution will be stopped.

What is each iteration in Ruby?

“Iterators” is the object-oriented concept in Ruby. In more simple words, iterators are the methods which are supported by collections(Arrays, Hashes etc.). Collections are the objects which store a group of data members. Ruby iterators return all the elements of a collection one after another.


2 Answers

take and drop have a variant take_while and drop_while where instead of providing a fixed number of elements you provide a block. Ruby will accumulate values from the receiver (in the case of take_while) as long as the block returns true. Your code could be rewritten as

array.take_while {|pair| pair.sum < foo}.map(&:sum)

This does mean that you calculate the sum of some of these pairs twice.

like image 90
Frederick Cheung Avatar answered Nov 08 '22 15:11

Frederick Cheung


In Ruby 2.0 there's Enumerable#lazy which returns a lazy enumerator:

sums = pairs.lazy.map { |a, b| a + b }.take_while { |pair_sum| pair_sum < some_max_value }.force

This avoids calculating the sums twice.

like image 25
Stefan Avatar answered Nov 08 '22 17:11

Stefan