multiplication table in Ruby - ruby
Can you please help me?
I'm solving an exercise in Ruby and the result has to be like this:
it 'multiplication table de 1 a 10' do
expect(ArrayUtils.tabuada(10)).to eq [
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20],
[3, 6, 9, 12, 15, 18, 21, 24, 27, 30],
[4, 8, 12, 16, 20, 24, 28, 32, 36, 40],
[5, 10, 15, 20, 25, 30, 35, 40, 45, 50],
[6, 12, 18, 24, 30, 36, 42, 48, 54, 60],
[7, 14, 21, 28, 35, 42, 49, 56, 63, 70],
[8, 16, 24, 32, 40, 48, 56, 64, 72, 80],
[9, 18, 27, 36, 45, 54, 63, 72, 81, 90],
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
]
end
it 'multiplication table de 1 a 3' do
expect(ArrayUtils.tabuada(3)).to eq [
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20],
[3, 6, 9, 12, 15, 18, 21, 24, 27, 30],
]
end
My code:
def self.tabuada(n)
(1..n).each do |element|
(1..n-1).each { |item| print "#{element * item}, " }
puts element * n
end
end
tabuada(3)
It's the result:
1, 2, 3
2, 4, 6
3, 6, 9
Any suggestion?
There are two issues in your code:
The test expects you to return a nested array, not to print the result
The inner loop always ends a 10, it is not depending on the n at all
I would start with something like this:
def self.tabuada(n)
(1..n).map do |n|
(1..10).map do |i|
n * i
end
end
end
Or:
def self.tabuada(n)
(1..n).map { |element| Array.new(10) { |i| (i + 1) * element } }
end
Good error messages are very important for a good testing framework.
The error messages should guide you to a solution.
It looks like you are using RSpec, which does indeed have good error messages. So, let's just look at the error message we get:
expected: [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [2, 4, 6, 8, 10, 12, 14, 16, 18, 20], [3, 6, 9, 12, 15, 18, 21, 24,...56, 64, 72, 80], [9, 18, 27, 36, 45, 54, 63, 72, 81, 90], [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]]
got: 1..10
(compared using ==)
So, the error message is telling us that RSpec expected to find an array, but instead your method returned a range. That's interesting, so let's look at what does your method return.
Well, in the absence of an explicit return keyword, a method definition body evaluates to the value of the last expression that was evaluated inside the method definition body. In your case, there is only a single expression inside the method, so the value of that expression is returned:
(1..n).each do |element|
(1..n-1).each { |item| print "#{element * item}, " }
puts element * n
end
What is the return value of (1..n).each? We can simply look at the documentation, and we see that Range#each returns self, i.e. the object that it was called on. So, the return value of your method will always be simply the range 1..n, and not the array of arrays that the test expects.
[Sidenote: I did, in fact, not look up what Range#each returns. Every implementation of each for every collection always returns self, that is part of the contract of each. This is one of the things you just learn over time when you program in Ruby.]
Let's do the simplest possible thing we can do to change the message. That is all we want to do. We don't want to fix everything and we don't want to take a huge step. We just want to make a tiny change that changes the error message.
If we look at the methods available to us, we find the method Enumerable#map, which transforms the elements and returns an array of the transformed elements. That actually sounds quite good, since transforming some numbers into a table of multiplied numbers is pretty much exactly what we want to do.
So, we just change the each to map. Nothing more:
(1..n).map do |element|
(1..n-1).each { |item| print "#{element * item}, " }
puts element * n
end
And then we see what happens:
got: [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil]
That is already better. We are expecting an array, and we are getting an array. Just the wrong array, but we already have the correct type! Before, we were getting a range, now we are getting an array.
Why is this array filled with nils, though? We want numbers! If we look at the documentation of map again, we see that the value of the block is used to fill the array. So, what is the value of the block?
Just as above, the value of the whole block is the value of the last expression evaluated inside the block. The last expression inside the block is
puts element * n
And if we look at the documentation of Kernel#puts, we can see that it does, indeed, return nil! But we want a number instead. Well, we already have a number: element * n is a number! So, instead of printing the number (which the problem description never asked for in the first place) and returning nil, let's just return the number. In other words, just remove the puts:
(1..n).map do |element|
(1..n-1).each { |item| print "#{element * item}, " }
element * n
end
And this is the result:
got: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
Again, one step closer. We want an array, we have an array. We want numbers, we have numbers. What we actually want, though is not an array of numbers, but an array-of-arrays-of-numbers. If you look closely, you can see that we have here is actually the last array of the array-of-arrays-of-numbers we are expecting (or to put it another way, it is the last row of the table).
Hey, but we already know something that can produce arrays: map can do that! Turning the outer each into a map gave us an array. Naturally, nesting a map within a map will give us nested arrays. So, let's just do that:
(1..n).map do |element|
(1..n-1).map { |item| print "#{element * item}, " }
element * n
end
Hmm … actually, that didn't change anything. It makes sense, really: the value of the block is the value of the last expression, and the last expression is element * n. The map before it does return an array, but we're not doing anything with that array, we are just throwing it away.
So, let's just as an experiment remove the element * n and see what happens:
got: [[nil, nil, nil, nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil, nil, nil, nil], [nil, ... nil], [nil, nil, nil, nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil, nil, nil, nil]]
One step forward, one step back, I guess. We wanted an array-of-arrays-of-numbers, originally we had an array-of-numbers and were missing the "nesting" part, now we have a nested array-of-arrays, but we lost the numbers.
But we already know the cause, since we figured it out once before: Kernel#print returns nil, so let's just delete it:
(1..n).map do |element|
(1..n-1).map { |item| "#{element * item}, " }
end
And this is the result:
expected: [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [2, 4, 6, 8, 10, 12, 14, 16, 18, 20], [3, 6, 9, 12, 15, 18, 21, 24,...56, 64, 72, 80], [9, 18, 27, 36, 45, 54, 63, 72, 81, 90], [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]]
got: [["1, ", "2, ", "3, ", "4, ", "5, ", "6, ", "7, ", "8, ", "9, "], ["2, ", "4, ", "6, ", "8, ", "10, "..., "63, ", "72, ", "81, "], ["10, ", "20, ", "30, ", "40, ", "50, ", "60, ", "70, ", "80, ", "90, "]]
Hey, that's actually pretty close! We want an array-of-arrays-of-numbers, and we have an array-of-arrays-of-strings-with-numbers-in-them. Let's see what happens when we remove the string and simply keep the number:
(1..n).map do |element|
(1..n-1).map { |item| element * item }
end
expected: [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [2, 4, 6, 8, 10, 12, 14, 16, 18, 20], [3, 6, 9, 12, 15, 18, 21, 24,...56, 64, 72, 80], [9, 18, 27, 36, 45, 54, 63, 72, 81, 90], [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]]
got: [[1, 2, 3, 4, 5, 6, 7, 8, 9], [2, 4, 6, 8, 10, 12, 14, 16, 18], [3, 6, 9, 12, 15, 18, 21, 24, 27], [4... 32, 40, 48, 56, 64, 72], [9, 18, 27, 36, 45, 54, 63, 72, 81], [10, 20, 30, 40, 50, 60, 70, 80, 90]]
Cool! We are only counting one too low. That is because we were originally having a special case for the last number, and we simply removed that special case. As we can see now, there is actually no reason for the special case, so we can just incorporate the last number into our normal flow of the code:
(1..n).map do |element|
(1..n).map { |item| element * item }
end
Congratulations! The first of the two tests passes! We only have the second test to worry about now, which gives this error:
expected: [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [2, 4, 6, 8, 10, 12, 14, 16, 18, 20], [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]]
got: [[1, 2, 3], [2, 4, 6], [3, 6, 9]]
Oh, I see what is going on here. In the first test, the multiplication table just happened to be square, so we implemented a square multiplication table. But actually, the rows are always supposed to be 10 columns wide, regardless of n, whereas we just assumed they were n wide because in the first test, n happened to be 10.
But that is easy to fix:
(1..n).map do |element|
(1..10).map { |item| element * item }
end
And now all of the tests pass!
You will notice that we get almost no output now. That is a standard programming philosophy: if everything is going well, do not print anything. Only print something when there is something wrong. If you print too much when things are going right, it will be hard to spot when something is going wrong. Also, you de-sensitize your users and they just start ignoring what is printed, because it isn't interesting to them.
That's why RSpec by default only prints a single green dot for every test that passes, and a single line summary at the end, and only prints large messages when things are going wrong:
..
Finished in 0.00701 seconds (files took 0.15231 seconds to load)
2 examples, 0 failures
There are, of course, many other ways to write this. What I wanted to show you, is how you could fix what you already had written, by doing nothing but blindly following RSpec's excellent error messages, and simply doing tiny little steps to change the error message into something different. You are not even trying to fix the error, you are only trying to change the error to something that is closer to your goal.
We didn't really have to think hard. The errors were pretty much telling us what to do. That is the hallmark of good error messages and good tests.
You can even do this when you have no code at all! There is a methodology called "Test-Driven Development" (TDD), which is centered around the idea that you write tests before you ever write the first line of code, and then develop your code by simply "listening to the errors".
In this case, it could look a little like this. We start with an empty file, and we get this error:
NameError:
uninitialized constant ArrayUtils
Remember, we are only trying to do the "simplest possible thing to change the error message". The error message says that the ArrayUtils constant is not initialized, so we just initialize it, nothing more:
ArrayUtils = nil
NoMethodError:
undefined method `tabuada' for nil:NilClass
Okay, now it is telling us that NilClass doesn't have a method called tabuada. All we do is the stupidest thing to change the error message: we add that method to NilClass:
class NilClass
def tabuada
end
end
Is this stupid? Yes, of course it is! But we're not trying to be clever here. We are, in fact, very much trying to be not clever. Debugging code is hard, harder than to write code. Which means that if you make your code as clever as you can, you are by definition not clever enough to debug it! So, let's stick with this for now.
ArgumentError:
wrong number of arguments (given 1, expected 0)
Okay, so let's just give it one:
def tabuada(_)
end
Now, we no longer get errors from Ruby but instead test failures from RSpec. That is already a good step:
expected: [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [2, 4, 6, 8, 10, 12, 14, 16, 18, 20], [3, 6, 9, 12, 15, 18, 21, 24,...56, 64, 72, 80], [9, 18, 27, 36, 45, 54, 63, 72, 81, 90], [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]]
got: nil
So, we're returning the wrong thing. We can easily fix that by returning what the test asks us to:
def tabuada(_)
[
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20],
[3, 6, 9, 12, 15, 18, 21, 24, 27, 30],
[4, 8, 12, 16, 20, 24, 28, 32, 36, 40],
[5, 10, 15, 20, 25, 30, 35, 40, 45, 50],
[6, 12, 18, 24, 30, 36, 42, 48, 54, 60],
[7, 14, 21, 28, 35, 42, 49, 56, 63, 70],
[8, 16, 24, 32, 40, 48, 56, 64, 72, 80],
[9, 18, 27, 36, 45, 54, 63, 72, 81, 90],
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
]
end
This passes the first test, obviously, but not the second. Now, we could add a conditional expression like this:
def tabuada(_)
if _ == 10
[
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20],
[3, 6, 9, 12, 15, 18, 21, 24, 27, 30],
[4, 8, 12, 16, 20, 24, 28, 32, 36, 40],
[5, 10, 15, 20, 25, 30, 35, 40, 45, 50],
[6, 12, 18, 24, 30, 36, 42, 48, 54, 60],
[7, 14, 21, 28, 35, 42, 49, 56, 63, 70],
[8, 16, 24, 32, 40, 48, 56, 64, 72, 80],
[9, 18, 27, 36, 45, 54, 63, 72, 81, 90],
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
]
else
[
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20],
[3, 6, 9, 12, 15, 18, 21, 24, 27, 30]
]
end
end
However, it should be clear that this is not the spirit of the problem. If there were more tests, we would have to add a new clause for every number. In particular, this should be working for infinitely many numbers.
So, we'll have rethink our approach. Let's go back. All we need is an array, so let's start with an array:
def tabuada(_)
[]
end
As a first step, our array needs to have as many rows as the argument. If you want to create an array with a certain number of elements, you can use Array::new:
def tabuada(_)
Array.new(_)
end
got: [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil]
The length is good. But the contents are not. The contents should also be an array:
def tabuada(_)
Array.new(_) { Array.new(10) }
end
got: [[nil, nil, nil, nil, nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil, nil, nil, nil, ni...l, nil, nil, nil, nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil]]
That's great, the result array already has the shape we want, now it's just missing the content. Array::new yields the index to the block, so we can use that to create the content:
def tabuada(_)
Array.new(_) { |a| Array.new(10) { |b| a * b }}
end
got: [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 2, 4, 6, 8, 10, 12, 14, 16, 18],...35, 42, 49, 56, 63], [0, 8, 16, 24, 32, 40, 48, 56, 64, 72], [0, 9, 18, 27, 36, 45, 54, 63, 72, 81]]
We are very close now! As I said above, Array::new yields the index, but Ruby array indices range from 0 to size-1, not 1 to size, so all that is left to do is add 1 to the index, or alternatively, use the successor:
def tabuada(_)
Array.new(_) { |a| Array.new(10) { |b| a.succ * b.succ }}
end
And now, all of our tests pass!
Now that we have passing tests comes a very important step: Refactoring. Refactoring means:
While the tests are passing,
in small, well-defined, reversible steps,
change the structure of the code without changing its behavior
to make it look like you had the whole design figured out from the beginning.
Our first refactoring is going to be the Rename Parameter Refactoring. When we introduced the parameter, we were ignoring it, so we gave it the standard name for an ignored parameter: _. But that is a terrible name, so we will rename it to n:
def tabuada(n)
Array.new(n) { |a| Array.new(10) { |b| a.succ * b.succ }}
end
And we run our tests again to make sure that we haven't broken anything.
Our next refactoring is going to be the Move Method Refactoring. We very stupidly followed the error message telling us that it couldn't find the method tabuada on nil, so we did the simplest possible thing and added tabuada to nil. But, that doesn't make much sense.
Instead, we want to add it to ArrayUtils, which at that point just happened to be nil.
Which means that first, we need to change ArrayUtils to something else. Most Ruby programmers would probably use a module, but for something that is not going to be mixed and only used as a container for singleton methods, I personally actually prefer an empty BasicObject. So, let's do that and then move the method to it:
ArrayUtils = BasicObject.new
def ArrayUtils.tabuada(n)
Array.new(n) {|a| Array.new(10) {|b| a.succ * b.succ } }
end
We can slightly shorten this to something like this:
def (ArrayUtils = BasicObject.new).tabuada(n)
Array.new(n) {|a| Array.new(10) {|b| a.succ * b.succ } }
end
Note that in both cases, in the first example where we started from your failing code and fixed it, and in the second example where we started from an empty file, we always worked in small simple steps:
Read the error message.
Understand the error.
Make the simples, smallest, stupidest change to the code to change just one aspect of the error message.
Read the new error message.
Understand the error.
Check that the new error is a step forward or at least sideward.
If not, revert the change and try something different.
Else, go to #3 and repeat until the tests pass.
Only when the tests pass: refactor to make it look as if we had known where we are going from the beginning.
The tests ensure that we are always moving forward. The small steps ensure that we always understand what we are doing, and when things go wrong, we know the problem can only be in the tiny piece of code we changed. They also ensure that when we have to revert, we are only losing a couple seconds worth of work.
Related
How to iterate an array every 30 items
I have an array of products with 234 items. I need to create another array with a pagination (every 10 items) example: [ [1,2,3,4,5,6,7,8,9,10], [1,2,3,4,5,6,7,8,9,10], [1,2,3,4,5,6,7,8,9,10], ... ] How can I solve this? I've tried in_groups_of but I don't have success.
You're looking for each_slice Whenever you have an array problem, check the Enumerable. in_groups_of is a Rails method and uses each_slice under the hood.
Just use Enumerable#each_slice [*1..34].each_slice(10).to_a # => # [ # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], # [11, 12, 13, 14, 15, 16, 17, 18, 19, 20], # [21, 22, 23, 24, 25, 26, 27, 28, 29, 30], # [31, 32, 33, 34] # ]
Remove n elements from array dynamically and add to another array
nums= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30] new_array=[] How do I grab every two items divisible by 5 and add them to a new array. This is the desired result: the new_array should now contain these values [[5,10],[15,20],[25,30]] Note: I want to do this without pushing them all into the array and then performing array.each_slice(2). The process should happen dynamically.
Try this new_array = nums.select { |x| x % 5 == 0 }.each_slice(2).entries No push involved.
Make a square multiplication table in Ruby
I got this question in an interview and got almost all the way to the answer but got stuck on the last part. If I want to get the multiplication table for 5, for instance, I want to get the output to be formatted like so: 1, 2, 3, 4, 5 2, 4, 6, 8, 10 3, 6, 9, 12, 15 4, 8, 12, 16, 20 5, 10, 15, 20, 25 My answer to this is: def make_table(n) s = "" 1.upto(n).each do |i| 1.upto(n).each do |j| s += (i*j).to_s end s += "\n" end p s end But the output for make_table(5) is: "12345\n246810\n3691215\n48121620\n510152025\n" I've tried variations with array but I'm getting similar output. What am I missing or how should I think about the last part of the problem?
You can use map and join to get a String in one line : n = 5 puts (1..n).map { |x| (1..n).map { |y| x * y }.join(', ') }.join("\n") It iterates over rows (x=1, x=2, ...). For each row, it iterates over cells (y=1, y=2, ...) and calculates x*y. It joins every cells in a row with ,, and joins every rows in the table with a newline : 1, 2, 3, 4, 5 2, 4, 6, 8, 10 3, 6, 9, 12, 15 4, 8, 12, 16, 20 5, 10, 15, 20, 25 If you want to keep the commas aligned, you can use rjust : puts (1..n).map { |x| (1..n).map { |y| (x * y).to_s.rjust(3) }.join(',') }.join("\n") It outputs : 1, 2, 3, 4, 5 2, 4, 6, 8, 10 3, 6, 9, 12, 15 4, 8, 12, 16, 20 5, 10, 15, 20, 25 You could even go fancy and calculate the width of n**2 before aligning commas : n = 11 width = Math.log10(n**2).ceil + 1 puts (1..n).map { |x| (1..n).map { |y| (x * y).to_s.rjust(width) }.join(',') }.join("\n") It outputs : 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66 7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88 9, 18, 27, 36, 45, 54, 63, 72, 81, 90, 99 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110 11, 22, 33, 44, 55, 66, 77, 88, 99, 110, 121
Without spaces between the figures, the result is indeed unreadable. Have a look at the % operator, which formats strings and numbers. Instead of s += (i*j).to_s you could write s += '%3d' % (i*j) If you really want to get the output formatted in the way you explained in your posting (which I don't find that much readable), you could do a s += "#{i*j}, " This leaves you with two extra characters at the end of the line, which you have to remove. An alternative would be to use an array. Instead of the inner loop, you would have then something like s += 1.upto(n).to_a.map {|j| i*j}.join(', ') + "\n"
You don't need to construct a string if you're only interested in printing the table and not returning the table(as a string). (1..n).each do |a| (1..n-1).each { |b| print "#{a * b}, " } puts a * n end
This is how I'd do it. require 'matrix' n = 5 puts Matrix.build(n) { |i,j| (i+1)*(j+1) }.to_a.map { |row| row.join(', ') } 1, 2, 3, 4, 5 2, 4, 6, 8, 10 3, 6, 9, 12, 15 4, 8, 12, 16, 20 5, 10, 15, 20, 25 See Matrix::build.
You can make it much shorter but here's my version. range = Array(1..12) range.each do |element| range.map { |item| print "#{element * item} " } && puts end
How to split a range into N parts
I am wondering how to split a range into N parts in ruby, While adding them to a hash with a zero based value for each range generated. For example: range = 1..60 p split(range, 4) #=> {1..15 => 0, 16..30 => 1, 31..45 => 2, 46..60 => 3} I've read How to return a part of an array in Ruby? for how to slice a range into an array, and a few others on how to convert the slices back into ranges, but I can't quite seem to piece all the pieces together to create the method I want. Thanks for the help
range = 1..60 range.each_slice(range.last/4).with_index.with_object({}) { |(a,i),h| h[a.first..a.last]=i } #=> {1..15=>0, 16..30=>1, 31..45=>2, 46..60=>3} The steps are as follows: enum0 = range.each_slice(range.last/4) #=> range.each_slice(60/4) # #<Enumerator: 1..60:each_slice(15)> You can convert this enumerator to an array to see the (4) elements it will generate and pass to each_with_index: enum0.to_a #=> [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], # [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30], # [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45], # [46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60]] enum1 = enum0.with_index #=> #<Enumerator: #<Enumerator: 1..60:each_slice(15)>:with_index> enum1.to_a #=> [[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 0], # [[16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30], 1], # [[31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45], 2], # [[46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60], 3]] enum2 = enum1.with_object({}) #=> #<Enumerator: #<Enumerator: #<Enumerator: 1..60:each_slice(15)> # :with_index>:with_object({})> enum2.to_a #=> [[[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 0], {}], # [[[16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30], 1], {}], # [[[31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45], 2], {}], # [[[46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60], 3], {}]] Carefully examine the return values for the calculations of enum1 and enum2. You might want to think of them as "compound" enumerators. The second and last element of each of enum2's four arrays is the empty hash that is represented by the block variable h. That hash will be constructed in subsequent calculations. enum2.each { |(a,i),h| h[a.first..a.last]=i } #=> {1..15=>0, 16..30=>1, 31..45=>2, 46..60=>3} The first element of enum2 that is passed by each to the block (before enum.each... is executed) is arr = enum2.next #=>[[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 0], {}] The block variables are assigned to the elements of arr using parallel assignment (sometimes called multiple assignment) (a,i),h = arr #=> [[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 0], {}] a #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] i #=> 0 h #=> {} The block calculation is therefore h[a.first..a.last]=i #=> h[1..15] = 0 Now h #=> {1..15=>0} The calculations are similar for each of the other 3 elements generated by enum2. The expression enum2.each { |(a,i),h| h[(a.first..a.last)]=i } could alternatively be written enum2.each { |((f,*_,l),i),h| h[(f..l)]=i }
How do I generate an array of pairwise distances in Ruby?
Say I have an array that represents a set of points: x = [2, 5, 8, 33, 58] How do I generate an array of all the pairwise distances?
x = [2, 5, 8, 33, 58] print x.collect {|n| x.collect {|i| (n-i).abs}}.flatten I think that would do it.
x.map{|i| x.map{|j| (i-j).abs } } gives [[0, 3, 6, 31, 56], [3, 0, 3, 28, 53], [6, 3, 0, 25, 50], [31, 28, 25, 0, 25], [56, 53, 50, 25, 0]] (format it like this by printing it with 'pp' instead of puts) and x.map{|i| x.map{|j| (i-j).abs } }.flatten gives [0, 3, 6, 31, 56, 3, 0, 3, 28, 53, 6, 3, 0, 25, 50, 31, 28, 25, 0, 25, 56, 53, 50, 25, 0] if you really want an array
If you really do want an array instead of a matrix, this is O(n^2/2) instead of O(n^2). result=[] x.each_index{|i| (i+1).upto(x.size-1){|j| result<<(x[i]-x[j]).abs}}