3

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 
3
  • "how would one write a nested for loop in ruby without writing the entire nested for loop"? You have to define the loop somehow. You can't define an incomplete loop and have Ruby run it because it would be a syntax error. Commented Jul 9, 2013 at 4:59
  • The code in the answer above contains parse error: the parser will see the <<a*b string as HEREDOC syntax instead of << and then a*b. A single space is required between them to separate the tokens and avoid the parse error. Commented Aug 23, 2013 at 5:47
  • @SasQ good point, fixed now. Commented Aug 24, 2013 at 2:15

7 Answers 7

7

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 :*} 
Sign up to request clarification or add additional context in comments.

3 Comments

It's an effective trick, but somehow, #repeated_permutation is not meant for this. +1 for teaching the noobs the method, but please, don't use it like this.
Your Matrix solution is more code, is an order of magnitude slower, and uses nearly twice as much RAM to achieve the same result. For a square matrix, #repeated_permutation is just fine.
this is the kind of thing I was looking for, thanks for teaching me a cool new trick!
4
(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

2 Comments

@BorisStitnicky would you consider this to be more efficient than the repeated_perutation method above? I would assume it would be slower given the intermedite arrays it would be creating and then flattening.
Benchmarking shows that #repeated_permutation is indeed faster and more efficient, likely for the reasons you guessed. It's effectively x.product(x).
3

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.

Comments

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

2 Comments

ahh, this is what I was originally trying to do, made me realise I should pay attention to the difference between map and each can anyone explain this though: (1..10).each {|a|a} => 1..10
#each will return the original object passed to it after iteration (in this case, a range). #map will collect the results of the block into an array and return it.
1

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 } 

1 Comment

I'm not sure if my invocation of enum_for is quite right, I'd have to check. If anyone else knows the right way, please edit.
0
(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 } 

Comments

0

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.