Mike H-R
Mike H-R

Reputation: 7815

What is an elegant and concise way of iterating over multiple arrays in ruby?

The thing I love about Ruby is its elegance: if we use inject or map along with take_while and select, we can chain blocks together to achieve a lot while writing little.

Sticking with the idea of single line solutions, how would one write a nested for loop in Ruby without writing the entire nested for loop? I feel it must be possible, I just can't for the life of me figure out what it is. I am looking for something like this:

10.times {|a| 10.times {|b| a*b}}

The only solution I can come up with that is at all elegant is nested for loops. Does anyone have a better solution?

array = []
for a in (1..10)
  for b in (1..10)
    array << a*b
  end
end

Upvotes: 3

Views: 735

Answers (7)

7stud
7stud

Reputation: 48599

The only solution I can come up with that is at all elegant is nested for loops

for-in loops call each() on the object to the right of in, so rubyists don't use for-in loops--they call each() directly on the object:

array = []

(1..10).each do |a|
  (1..3).each do |b|
    array << a*b
  end
end

Sticking with the idea of single line solutions

Doing that will nearly guarantee you don't write elegant ruby code--just look at the proposed solutions.

Upvotes: 3

Alex D
Alex D

Reputation: 30445

Looking at all the answers to this question, I don't feel any of them appear more "elegant" or easier to read than the OP's nested for loops. If you want a less verbose notation for nested iteration, I don't think you'll do better than defining your own shorthand. Something like:

 module Enumerable
   def combinations(*others)
     return enum_for(:combinations,*others) if not block_given?
     return if self.empty?
     if others.empty?
       self.each { |x| yield [x] }
     else
       others.first.combinations(*others.drop(1)) { |a| self.each { |x| yield (a + [x]) }}
     end
   end
 end

Having defined this utility method, you can write your example of nested iteration as:

array = []
(1..10).combinations(1..10) { |a,b| array << a*b }

Upvotes: 1

Boris Stitnicky
Boris Stitnicky

Reputation: 12578

Facing problems like these, remember your high school calculus:

a = *1..10
b = *1..10
require 'matrix'

Matrix.column_vector( a ) * Matrix[ b ]
# or equivalent
Matrix[ a ].transpose * Matrix[ b ]

Matrix is a part of Ruby stdlib and every serious Ruby speaker should learn its interface.

Upvotes: 0

Chris Heald
Chris Heald

Reputation: 62648

Array has some cool methods.

 Array(1..10).repeated_permutation(2).map {|a, b| a*b }

#repeated_permutation will take an array and generate an array of all permutations of that array of a given length (2, in this case), permitting for repetition (ie, [1,1]). We can then just map the product of each pair into a final array.

You can generalize this by using inject(:*). This will take the resultant permutations and multiply all the elements of each. For example, to generate (1*1*1*1)..(10*10*10*10) (resulting in an output set of 10,000 elements!):

Array(1..10).repeated_permutation(4).map {|v| v.inject :*}

Upvotes: 7

Matt Dressel
Matt Dressel

Reputation: 2194

(1..10).inject([]) { |result,a| result + (1..10).to_a.map { |b| a*b } }

Or

def arithmetic(range, &block)
  range.inject([]) { |result,a| result + range.to_a.map { |b| block.call(a,b) } }
end

range = (1..10)
arithmetic(range) {|a,b| a*b }
arithmetic(range) {|a,b| a+b }

Upvotes: 0

falsetru
falsetru

Reputation: 368954

(1..10).to_a.product((1..10).to_a).map { |a,b| a*b }

http://ruby-doc.org/core-2.0/Array.html#method-i-product

Upvotes: 4

Platinum Azure
Platinum Azure

Reputation: 46183

arr = (1..10).map {|a| (1..10).map {|b| a*b}}.flatten

Upvotes: 2

Related Questions