Might be something like this:
rest = hash.reject { |k,v| v.nil? }.to_a
wildvalues.map { |wv| Hash[rest + wildkeys.zip(wv)] }
or even
wildvalues.map { |wv| hash.merge(Hash[wildkeys.zip(wv)]) }
Question
Given a set of possible values, and a hash of arbitrary number of values, how can I replace every nil
value with every possible combination of the possible values?
For example:
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' }]
I can find the keys that need to be replaced:
wildkeys = hash.select{ |k,v| v.nil? }.map(&:first)
#=> [:y, :z]
And thus I can 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"]]
But I can't think of a simple way to merge these two into the original.
Solution
Might be something like this:
rest = hash.reject { |k,v| v.nil? }.to_a
wildvalues.map { |wv| Hash[rest + wildkeys.zip(wv)] }
or even
wildvalues.map { |wv| hash.merge(Hash[wildkeys.zip(wv)]) }
OTHER TIPS
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
Another way:
Code
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