2014-01-25 1 views
1

Учитывая набор возможных значений и хеш произвольного количества значений, как я могу заменить каждое значение nil любой возможной комбинацией возможных значений?Заполнение подстановочных значений в хэше с любым возможным значением

Например:

values = %w[a b c] 
hash = { x:1, y:2, z:nil } 
fill_wildcards(hash, values) 
#=> [{ x:1, y:2, z:'a' }, 
#=> { x:1, y:2, z:'b' }, 
#=> { x:1, y:2, z:'c' }] 

hash = { x:1, y:nil, z:nil } 
fill_wildcards(hash, values) 
#=> [{ x:1, y:'a', z:'a' }, 
#=> { x:1, y:'a', z:'b' }, 
#=> { x:1, y:'a', z:'c' }, 
#=> { x:1, y:'b', z:'a' }, 
#=> { x:1, y:'b', z:'b' }, 
#=> { x:1, y:'b', z:'c' }, 
#=> { x:1, y:'c', z:'a' }, 
#=> { x:1, y:'c', z:'b' }, 
#=> { x:1, y:'c', z:'c' }] 

я могу найти ключи, которые должны быть заменены:

wildkeys = hash.select{ |k,v| v.nil? }.map(&:first) 
#=> [:y, :z] 

И таким образом я могу find all the permutations of values needed:

wildvalues = values.repeated_permutation(wildkeys.length).to_a 
#=> [["a", "a"], ["a", "b"], ["a", "c"], ["b", "a"], 
#=> ["b", "b"], ["b", "c"], ["c", "a"], ["c", "b"], ["c", "c"]] 

Но я не может придумать простой способ объединить эти два в оригинал.

ответ

2

Может быть что-то вроде этого:

rest = hash.reject { |k,v| v.nil? }.to_a 
wildvalues.map { |wv| Hash[rest + wildkeys.zip(wv)] } 

или даже

wildvalues.map { |wv| hash.merge(Hash[wildkeys.zip(wv)]) } 
+0

Это последнее является совершенным. Благодарю. – Phrogz

2
def fill_wildcards(hsh, values) 
    values.repeated_permutation(hsh.values.count(nil)).to_a.map {|combo| hsh.each_with_object(hsh.dup) {|(k,v),hsh| hsh[k] = combo.shift unless v } } 
end 
1

Другой способ:

Код

def doit(hash, values) 
    a = hash.map { |k,v| [k].product(v ? [v] : values) } 
    a.shift.product(*a).map(&:to_h) 
end 

Demo

values = %w[a b c] 

hash = { x:1, y:2, z:nil } 
p doit(hash, values) 
    #=> [{:x=>1, :y=>2, :z=>"a"}, 
    # {:x=>1, :y=>2, :z=>"b"}, 
    # {:x=>1, :y=>2, :z=>"c"}] 

hash = { x:1, y:nil, z:nil } 
p doit(hash, values) 
    #=> [{:x=>1, :y=>"a", :z=>"a"}, 
    # {:x=>1, :y=>"a", :z=>"b"}, 
    # {:x=>1, :y=>"a", :z=>"c"}, 
    # {:x=>1, :y=>"b", :z=>"a"}, 
    # {:x=>1, :y=>"b", :z=>"b"}, 
    # {:x=>1, :y=>"b", :z=>"c"}, 
    # {:x=>1, :y=>"c", :z=>"a"}, 
    # {:x=>1, :y=>"c", :z=>"b"}, 
    # {:x=>1, :y=>"c", :z=>"c"}] 

    hash = { x:nil, y:nil, z:nil } 
    p doit(hash, values).size #=> 27