Ruby哈希组合

6

我正在为一款电商应用程序尝试将一个选项哈希(每个选项都有一个选择数组)转换为表示这些选择组合的哈希数组。例如:

# Input:
{ :color => [ "blue", "grey" ],
  :size  => [ "s", "m", "l" ] }

# Output:
[ { :color => "blue", :size => "s" },
  { :color => "blue", :size => "m" },
  { :color => "blue", :size => "m" },
  { :color => "grey", :size => "s" },
  { :color => "grey", :size => "m" },
  { :color => "grey", :size => "m" } ]

输入框内可能包含一个未确定数量选项的附加选项,但它只会嵌套1级。任何

4个回答

7
上述内容的一个变体:
input = { color: [ "blue", "grey" ],
          size:  [ "s", "m", "l" ],
          wt:    [:light, :heavy] }

keys = input.keys
  #=> [:color, :size, :wt]
values = input.values
  #=> [["blue", "grey"], ["s", "m", "l"], [:light, :heavy]]
values.shift.product(*values).map { |v| Hash[keys.zip(v)] }
  #=> [{:color=>"blue", :size=>"s", :wt=>:light},
  #    {:color=>"blue", :size=>"s", :wt=>:heavy},
  #    {:color=>"blue", :size=>"m", :wt=>:light},
  #    {:color=>"blue", :size=>"m", :wt=>:heavy},
  #    {:color=>"blue", :size=>"l", :wt=>:light},
  #    {:color=>"blue", :size=>"l", :wt=>:heavy},
  #    {:color=>"grey", :size=>"s", :wt=>:light},
  #    {:color=>"grey", :size=>"s", :wt=>:heavy},
  #    {:color=>"grey", :size=>"m", :wt=>:light},
  #    {:color=>"grey", :size=>"m", :wt=>:heavy},
  #    {:color=>"grey", :size=>"l", :wt=>:light},
  #    {:color=>"grey", :size=>"l", :wt=>:heavy}]

6

您可以尝试以下方法:

ary = input.map {|k,v| [k].product v}
output = ary.shift.product(*ary).map {|a| Hash[a]}

结果:

[
  {:color=>"blue", :size=>"s"},
  {:color=>"blue", :size=>"m"},
  {:color=>"blue", :size=>"l"},
  {:color=>"grey", :size=>"s"},
  {:color=>"grey", :size=>"m"},
  {:color=>"grey", :size=>"l"}
]

1
我认为你的意思是使用 shift 而不是 unshift(如果没有给出参数,则不执行任何操作)。另外,值得一提的是,在 Ruby 2+ 中,您可以将最后一个 map 替换为 map(&:to_h),因此:ary.shift.product(*ary).map(&:to_h) - Jordan Running
@Jordan - 当然我是指shift,现在已经很晚了。 :P 谢谢你指出来。 - BroiSatse
@BroiSatse 这是一个很好的解决方案,尽管对于初学者来说可能有点难以理解。个人认为使用解构赋值比使用 shift 更加优雅:first, *rest = input.map {|k,v| [k].product v }; output = first.product(*rest).map(&:to_h)。但这只是我的个人看法。;) - Jordan Running

3

在这里,您基本上正在尝试计算组合,这意味着需要两个层级的迭代,并使用一种聚合这些操作结果的方法:

input = {:color=>["blue", "grey"], :size=>["s", "m", "l"]}

combinations = input[:color].flat_map do |color|
  input[:size].collect do |size|
    { color: color, size: size }
  end
end

puts combinations.inspect
# => [{:color=>"blue", :size=>"s"}, {:color=>"blue", :size=>"m"}, {:color=>"blue", :size=>"l"}, {:color=>"grey", :size=>"s"}, {:color=>"grey", :size=>"m"}, {:color=>"grey", :size=>"l"}]

在这里,flat_map变得很有用,因为它会折叠内部扩展的结果。


谢谢。我正在尝试找到一种更通用的方法来完成这个任务,而不依赖于输入哈希表的键,因为那些将是用户输入。 - dvanderb
1
@mu-is-too-short 你说得对。同时,我认为当我说“输入可能包含内部具有未确定数量选项的附加选项”时,这是暗示的。我不认为这个解决方案能够完全处理这种情况。 - dvanderb

1

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

require "ocg"

generator = OCG.new(
  :color => %w[blue grey],
  :size  => %w[s m l]
)

puts generator.next until generator.finished?

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


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