2014-09-03 2 views
4

Для приложения электронной коммерции я пытаюсь превратить хэш опций, каждый с массивом вариантов, в массив хешей, представляющих комбинации этих вариантов. Например:Ruby хэш-комбинации

# 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" } ] 

Input могут иметь дополнительные опции внутри него с неопределенным количеством вариантов для каждого из них, но это будет только когда-либо быть вложенными 1 уровень глубокой. Любой

ответ

4

Вы можете попробовать:

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' (который ничего не делает, если аргументы не заданы). И FWIW в Ruby 2+ вы можете заменить последнюю «карту» на «map (&: to_h)», ergo: 'ary.shift.product (* ary) .map (&: to_h)'. –

+0

похоже на это решение с работами 'shift' – dvanderb

+0

@ Иордан - конечно, я имел ввиду сдвиг, уже поздно. : P Спасибо, что указали это. – BroiSatse

2

Вы в основном пытаетесь вычислить комбинации здесь, и это означает, что два уровня итерации с путем объединения результатов этих операций:

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 пригождается как она сворачивает результаты внутреннего расширения.

+0

Спасибо. Я пытаюсь найти более общий способ сделать это, не полагаясь на ключи входного хэша, потому что это будут пользовательские входы – dvanderb

+0

@dvanderb. Тогда, возможно, вы должны задать этот вопрос ... –

+1

@ mu-is-too -Затем ты прав. В то же время я думал, что это подразумевалось, когда я сказал: «Ввод может иметь дополнительные опции внутри него с неопределенным количеством вариантов для каждого из них». Я не думаю, что это решение вполне справится с этим сценарием. – dvanderb

6

Вариант выше:

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}] 
Смежные вопросы