Все возможные комбинации из хэша массивов в Ruby - PullRequest
18 голосов
/ 20 марта 2012

Что у меня есть:

Допустим, у меня есть такой хеш, с различными значениями, принадлежащими одному параметру.

a = {}
a[:bitrate] = ["100", "500", "1000"]
a[:fps] = ["15", "30"]
a[:qp] = ["20", "30"]

Что мне нужно:

Мне нужен какой-то способ для итеративного получения всех возможных комбинаций этих значений, поэтому со всеми парами параметр / значение:

  • bitrate = 100, fps = 15, qp = 20
  • bitrate = 500, fps = 15, qp = 30
  • ...

Число параметров (то есть ключей) и количество значений (то есть длина массивов значений) заранее неизвестны. В идеале я бы сделал что-то вроде:

a.foo do |ret|
  puts ret.keys   # => ["bitrate", "fps", "qp"]
  puts ret.values # => ["100", "15", "20"]
end

… где блок вызывается для каждой возможной комбинации. Как я могу определить foo?


Что мне (вероятно) не нужно:

Теперь я знаю это: Объединить массив массивов во все возможные комбинации, только вперед, в Ruby , предлагая что-то вроде:

a.first.product(*a[1..-1]).map(&:join)

Но это работает со значениями и массивами только в массивах, и мне нужна оригинальная ссылка на имя параметра.

Ответы [ 3 ]

29 голосов
/ 20 марта 2012
a = {}
a[:bitrate] = ["100", "500", "1000"]
a[:fps] = ["15", "30"]
a[:qp] = ["20", "30"]

def product_hash(hsh)
  attrs   = hsh.values
  keys    = hsh.keys
  product = attrs[0].product(*attrs[1..-1])
  product.map{ |p| Hash[keys.zip p] }
end

product_hash(a)

вы получите

[{:bitrate=>"100", :fps=>"15", :qp=>"20"},
 {:bitrate=>"100", :fps=>"15", :qp=>"30"},
 {:bitrate=>"100", :fps=>"30", :qp=>"20"},
 {:bitrate=>"100", :fps=>"30", :qp=>"30"},
 {:bitrate=>"500", :fps=>"15", :qp=>"20"},
 {:bitrate=>"500", :fps=>"15", :qp=>"30"},
 {:bitrate=>"500", :fps=>"30", :qp=>"20"},
 {:bitrate=>"500", :fps=>"30", :qp=>"30"},
 {:bitrate=>"1000", :fps=>"15", :qp=>"20"},
 {:bitrate=>"1000", :fps=>"15", :qp=>"30"},
 {:bitrate=>"1000", :fps=>"30", :qp=>"20"},
 {:bitrate=>"1000", :fps=>"30", :qp=>"30"}]

Вы также можете добавить новый ключ к вашему хешу.

a = {}
a[:bitrate] = ["100", "500", "1000"]
a[:fps] = ["15", "30"]
a[:qp] = ["20", "30"]
a[:bw] = [true, false]

product_hash(a)

#=>
[{:bitrate=>"100", :fps=>"15", :qp=>"20", :bw=>true},
 {:bitrate=>"100", :fps=>"15", :qp=>"20", :bw=>false},
 {:bitrate=>"100", :fps=>"15", :qp=>"30", :bw=>true},
 {:bitrate=>"100", :fps=>"15", :qp=>"30", :bw=>false},
 {:bitrate=>"100", :fps=>"30", :qp=>"20", :bw=>true},
 {:bitrate=>"100", :fps=>"30", :qp=>"20", :bw=>false},
 {:bitrate=>"100", :fps=>"30", :qp=>"30", :bw=>true},
 {:bitrate=>"100", :fps=>"30", :qp=>"30", :bw=>false},
 {:bitrate=>"500", :fps=>"15", :qp=>"20", :bw=>true},
 {:bitrate=>"500", :fps=>"15", :qp=>"20", :bw=>false},
 {:bitrate=>"500", :fps=>"15", :qp=>"30", :bw=>true},
 {:bitrate=>"500", :fps=>"15", :qp=>"30", :bw=>false},
 {:bitrate=>"500", :fps=>"30", :qp=>"20", :bw=>true},
 {:bitrate=>"500", :fps=>"30", :qp=>"20", :bw=>false},
 {:bitrate=>"500", :fps=>"30", :qp=>"30", :bw=>true},
 {:bitrate=>"500", :fps=>"30", :qp=>"30", :bw=>false},
 {:bitrate=>"1000", :fps=>"15", :qp=>"20", :bw=>true},
 {:bitrate=>"1000", :fps=>"15", :qp=>"20", :bw=>false},
 {:bitrate=>"1000", :fps=>"15", :qp=>"30", :bw=>true},
 {:bitrate=>"1000", :fps=>"15", :qp=>"30", :bw=>false},
 {:bitrate=>"1000", :fps=>"30", :qp=>"20", :bw=>true},
 {:bitrate=>"1000", :fps=>"30", :qp=>"20", :bw=>false},
 {:bitrate=>"1000", :fps=>"30", :qp=>"30", :bw=>true},
 {:bitrate=>"1000", :fps=>"30", :qp=>"30", :bw=>false}]
2 голосов
/ 14 февраля 2013

Только к твоему сведению. Я выбрал подход fl00r и залатал его обезьяной. Мне нравится это немного лучше.

class Hash
  def product
    product = values[0].product(*values[1..-1])
    product.map{|p| Hash[keys.zip p]}
  end
end
0 голосов
/ 25 мая 2016

Я считаю, что ответ fl00r почти идеален, но имеет недостаток.Предполагается, что у hsh.values и hsh.keys будет соответствующий порядок, который, насколько я знаю, не гарантирован.Так что вам, вероятно, понадобится дополнительный шаг, чтобы это сделать.Может быть что-то вроде:

def product_hash(hsh)
  keys  = hsh.keys
  attrs = keys.map { |key| hsh[key] }
  product = attrs[0].product(*attrs[1..-1])
  product.map{ |p| Hash[keys.zip p] }
end

Но fl00r может исправить меня, если я ошибаюсь.

...