Roman Podolski
Roman Podolski

Reputation: 329

Permutations of Boolean values in ruby hash

I need to implement a function which gets a array with Symbols as parameters like [:a :b :c], and then returns a array which contains all possible permutations of boolean values mapped to those symbols in a hash

like

[{a: true, b: true, c: true},
{a: false, b: true, c: true},
{a: true, b: false, c: true},
{a: true, b: true, c: false},
{a: true, b: false, c: false},
{a: false, b: false, c: true},
{a: false, b: true, c: false},
{a: false, b: false, c: false}]

what is a elegant way to implement such a thing in ruby?

Upvotes: 3

Views: 2326

Answers (3)

Cary Swoveland
Cary Swoveland

Reputation: 110665

Another way is to use recursion:

def permute(arr)
  first, *rest = arr
  return [{ first=>:true }, { first=>:false }] if rest.empty? 
  permute(rest).flat_map { |h| [{ first=>:true }.merge(h), { first=>:false }.merge(h)] } 
end

permute([:a, :b, :c])
  #=> [{:a=>:true,   :b=>:true,   :c=>:true }, {:a=>:false, :b=>:true,  :c=>:true },
  #    {:a=>:true,   :b=>:false,  :c=>:true }, {:a=>:false, :b=>:false, :c=>:true },
  #    {:a=>:true,   :b=>:true,   :c=>:false}, {:a=>:false, :b=>:true,  :c=>:false},
  #    {:a=>:true,   :b=>:false,  :c=>:false}, {:a=>:false, :b=>:false, :c=>:false}] 

Upvotes: 2

Wand Maker
Wand Maker

Reputation: 18762

We can take advantage of fact that pattern of true/false in output hash follows binary number sequence between 0 to (2ary.size-1), and do something like below

require "pp"

ary = [:a,:b,:c]

result = (0...2**ary.size).map do |num|
  bool_array = (0...ary.size).map do |bit_pos| 
    num[bit_pos].zero?
  end
  ary.zip(bool_array).to_h 
end

pp result
#=> [{:a=>false, :b=>false, :c=>false},
#    {:a=>true, :b=>false, :c=>true},
#    {:a=>false, :b=>true, :c=>true},
#    {:a=>true, :b=>true, :c=>true},
#    {:a=>false, :b=>false, :c=>false},
#    {:a=>true, :b=>false, :c=>true},
#    {:a=>false, :b=>true, :c=>true},
#    {:a=>true, :b=>true, :c=>true}]

Upvotes: 4

Stefan
Stefan

Reputation: 114138

I use repeated_permutation for this task:

[true, false].repeated_permutation(3)
             .map { |a, b, c| { a: a, b: b, c: c } }
#=> [{:a=>true, :b=>true, :c=>true},
#    {:a=>true, :b=>true, :c=>false},
#    {:a=>true, :b=>false, :c=>true},
#    {:a=>true, :b=>false, :c=>false},
#    {:a=>false, :b=>true, :c=>true},
#    {:a=>false, :b=>true, :c=>false},
#    {:a=>false, :b=>false, :c=>true},
#    {:a=>false, :b=>false, :c=>false}]

or with an array of keys:

keys = %i(a b c)
[true, false].repeated_permutation(keys.size)
             .map { |values| keys.zip(values).to_h }

Upvotes: 10

Related Questions