tags:

views:

93

answers:

5

Hi,

let's say I have the following array:

arr = [[5,1], [2,7]]

and I want to find the minimum comparing the second element. i.e. The minimum will be [5,1] (since 1 is less than 7). I can use the following code:

arr.min {|a,b| a[1] <=> b[1]}

For calculating the maximum I can do the same:

arr.max {|a,b| a[1] <=> b[1]}

That gives [2,7]. Now, what I do not want is repeat myself since I am basically given all the time the same block. I would like to have that block somewhere and provide it to the min/max function. I hoped something like:

blo = lambda {|a,b| a[1] <=> b[1]}
arr.min blo

but didn't really work... so didn't work any of the other things I tried. Any Idea on how can I do this?

+12  A: 

Use the & operator to turn a Proc object into a block.

arr.min &blo
sepp2k
The only one, who reads question carefully.
Nakilon
This is exactly what I was looking for. Thanks a lot for the reply!
Cristobal Viedma
+3  A: 

how about this?

=> [[5, 4], [9, 5], [2, 7]]
>> arr.sort!{|x,y| x[1]<=>y[1] }
=> [[5, 4], [9, 5], [2, 7]]
>> min,max=arr[0],arr[-1]
=> [[5, 4], [2, 7]]
ghostdog74
+6  A: 

@sepp2k's answer is the more general one, but in your specific case, I would just use

arr.min_by(&:last)
arr.max_by(&:last)

since that is much more obvious than all those curly braces and square brackets and array indices floating around.

Jörg W Mittag
This one also works ;)
Cristobal Viedma
+2  A: 

A more general solution to problems like this is to avoid nested arrays entirely and use a class instead. You can then define the <=> operator for that class, giving you access to all the functions in the Comparable mixin (http://ruby-doc.org/core/classes/Comparable.html) gives you the <, <=, ==, >=, and > operators and the method 'between?'

This is just an example, in real life you would use classes that describe what they store:

class Duo

  include Comparable

  def initialize( a, b )
      @a = a
      @b = b
  end

  def <=>(rhs)
      @b <=> rhs.b
  end

end

If you have an array of Duo object you can then use the min, max, and sort functions without having to define the comparison operator. So...

@a = Duo.new( 1, 10 )
@b = Duo.new( 2, 5 )
@c = Duo.new( 3, 1 )

[ @a, @b, @c ].sort

would return the array [ @c, @b, @a ]

And

[@a, @b, @c].max

would return @a

This is much more the 'Ruby Way' than nested data-structures with logic that relies on positions in arrays. It takes slightly more work at the start, but you'll find it much better in the long run.

Ruby is a very object oriented programming language and provides very powerful tools for you to use. I thoroughly recommend reading a book like "The Ruby Programming Language" or "The Ruby Way" to get a proper overview of the power of the language.

Paul Leader
The name `Duo` doesn't indicate it'd sort by the last element. What if someone else created a `Duo` that sorted by the first element for their part of the project, leading to two different `Duo`s?
Andrew Grimm
Well that's always going to be a problem in any code you write, if you create a class and someone else also modifies it then you have a problem. But that isn't a reason not to do it. Of course this was just an example. In real life you would have a much more descriptive class name that actually described what was being stored, not just a generic two item store.
Paul Leader
Hi, thank you very much for your reply. I like it a lot and I agree with you, on the long run sometimes is better. In this occasion for me however, it was quicker just to pass the block.
Cristobal Viedma
+3  A: 

If all that you need is minimum and maximum, you might use Enumerable#minmax method and calculate both at once:

min, max = arr.minmax {|a,b| a[1] <=> b[1]}
#=> [[5, 1], [2, 7]]
min
#=> [5, 1]
max
#=> [2, 7]

Edit: Hell, I just noticed there is also minmax_by, so you can combine it with last method, and have:

min, max = arr.minmax_by &:last
Mladen Jablanović