The least of them: minimum (and maximum) array values (plural!)

Every once in a while, I find myself wanting to do things like this:

017:0> %w(one two four six).find_all_min {|w| w.length}
["one", "two", "six"]
018:0> [3, 2, 1, 3, 1, 1, 3, 2].find_all_max
[3, 3, 3]

I want the values from an array that are the most (or least) of some property. And the array and property are arbitrary. Always.

I know I could just do something like:

009:0> array = %w(1 2 3 3)
["1", "2", "3", "3"]
010:0> max = array.map {|x| x.to_i}.max
3
011:0> array.find_all {|x| x.to_i == max}
["3", "3"]

But it looks clunky to me. What I really want are methods to do that for me, and I want methods that are a little more complicated, and generated via module_eval… and using a Proc. What I want, in short, is this:

module Enumerable
  [['min','-1'], ['max','1']].each do |name, comparator|

    module_eval %{
      def find_all_#{name}
        block = Proc.new do |object|
          block_given? ? yield(object) : object
        end

        self.inject([]) do |result, element|
          if result.empty?
            result << element
          else
            case block[element] <=> block[result.first]
            when 0
              result << element
            when #{comparator}
              [element]
            else
              result
            end
          end
        end
      end
    }
  end
end

It’s not the best way to handle this situation, but I’m on a metaprogramming diet, because that’s something I don’t have enough practice in. So I’m doing it more than I need to. I’m trying to get a feel of when a Proc is called for (har har). Given the way I’m doing the inject, I feel that the Proc is a good move. Maybe I’m wrong.

Leave a Comment