Array#product For Ruby 1.8

2008/02/17 7:07am

Ruby 1.9 には配列同士の組み合わせを列挙する Array#product があるらしいのだが、Ruby 1.8 でも使いたくなったので実装してみた。

class Array

  # Array#product
  # -------------
  # Returns the cartesian product of the receiver and the arrays given as arguments.
  #
  # Usage:
  #   [1, 2, 3].product([4, 5])     # => [[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [3, 5]]
  #   [1, 2].product([1, 2])        # => [[1, 1], [1, 2], [2, 1], [2, 2]]
  #   [1, 2].product([3, 4],[5, 6]) # => [[1, 3, 5], [1, 3, 6], [1, 4, 5], [1, 4, 6],
  #                                 #     [2, 3, 5], [2, 3, 6], [2, 4, 5], [2, 4, 6]]
  #   [1, 2].product()              # => [[1], [2]]
  #   [1, 2].product([])            # => []
  #
  #   [1] * [2, 3]                  # => [[1, 2], [1, 3]]
  #   [1, 2] * [3, 4]               # => [[1, 3], [1, 4], [2, 3], [2, 4]]
  #
  def product(*others)
    arrays = [ self.map {|i| [i] } ].concat(others)
    arrays.inject do |result, item|
      (result * item).map do |t|
        t[0].dup << t[1]
      end
    end
  end

  def multiplication_operator_with_product(other)
    unless other.kind_of? Array
      multiplication_operator_without_product(other)
    else
      self.inject([]) do |ret, i|
        ret.concat( other.map {|j| [i, j]} )
      end
    end
  end

  alias_method :multiplication_operator_without_product, :*
  alias_method :*, :multiplication_operator_with_product

end

コメントにも書いてあるが、実行例は以下のとおり。

>> [1, 2, 3].product([4, 5])
=> [[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [3, 5]]
>> [1, 2].product([3, 4],[5, 6])
=> [[1, 3, 5], [1, 3, 6], [1, 4, 5], [1, 4, 6], [2, 3, 5], [2, 3, 6], [2, 4, 5], [2, 4, 6]]
>> [1, 2] * [3, 4]
=> [[1, 3], [1, 4], [2, 3], [2, 4]]

長らく放置気味だった Shipplr にもアップしておいた