Ruby中从数组哈希中获取所有可能的组合

22

我的情况:

假设我有一个这样的哈希表,其中包含属于一个参数的各种值。

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)

但是这只对值和数组中的数组起作用,我需要参数名称的原始引用。

4个回答

32
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

顺便说一下,我采用了fl00r的方法并对其进行了猴子补丁。我认为这样更好一些。

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

1

请尝试使用 OCG选项组合生成器。

require "ocg"

generator = OCG.new(
  :bitrate => %w[100 500 1000],
  :fps => %w[15 30],
  :qp => %w[20 30]
)

puts generator.next until generator.finished?

生成器包含更多功能,可帮助您处理其他选项。


0
我认为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可以纠正我。

对于 Ruby >= 1.9,它们的顺序是相同的。 - fl00r
这是否由语言规范保证,还是仅适用于MRI?在两种情况下,您能否指向确保此事的文档?我从不知道在哪里搜索这样的东西。谢谢。 - Ancor Gonzalez Sosa
哈希表按照插入键的顺序枚举其值。请参考:http://ruby-doc.org/core-2.4.1/Hash.html - fl00r

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接