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.

Was it helpful?

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top