From: Ruben Date: 2004-05-13T21:47:20+09:00 Subject: Re: How to make combinations of an array to produce all possible expressions? > I have an array 'conds', which contains some sub-expressions for an > xpath query: > > conds = ["@title='Foo'", "@edition='Bar'", "@date='20040513'"] > Something i made some weeks ago seems related to this. In my case, i wanted to generate a number of combinations by assigning a set of possible values to a number of 'fields' (you also seem to have fields and values you want to assign to them). I don't know whether this is relevant to you, but it might give you some ideas... So, the first thing i had, was a class 'Profile' which is merely a placeholder for the 'fields'. The constructor of 'Profile' takes a Hash as parameter. This Hash is used to assign a certain value to certain fields (the fields are just instance variables), fields not included as key in the Hash recieve a default value. I needed to build a lot of these Profiles and my idea was to define a number of 'constraints' on certain fields, like this: constraints = {"size" => [22,23,24,25], "tidy" => [true,false], "new" => [true], "freq" => [[2,2],[2,4],[2,8]] } With those constraints, i want to generate Profiles initialized with the following Hashes: {"size" => 22, "tidy" => true, "new" => true, "freq" => [2,2]} {"size" => 22, "tidy" => true, "new" => true, "freq" => [2,4]} {"size" => 22, "tidy" => true, "new" => true, "freq" => [2,8]} {"size" => 22, "tidy" => false, "new" => true, "freq" => [2,2]} {"size" => 22, "tidy" => false, "new" => true, "freq" => [2,4]} {"size" => 22, "tidy" => false, "new" => true, "freq" => [2,8]} {"size" => 23, "tidy" => true, "new" => true, "freq" => [2,2]} {"size" => 23, "tidy" => true, "new" => true, "freq" => [2,4]} {"size" => 23, "tidy" => true, "new" => true, "freq" => [2,8]} {"size" => 23, "tidy" => false, "new" => true, "freq" => [2,2]} {"size" => 23, "tidy" => false, "new" => true, "freq" => [2,4]} {"size" => 23, "tidy" => false, "new" => true, "freq" => [2,8]} ... To do this, i made the following 'GeneratingHash'. You construct it with a constraints-Hash, and then you can use it as an iterator with the 'each' method. You could use the same idea to generate combinations of Arrays. A good thing about this way of working is that you can use it to generate one Hash at a time, do stuff, forget about the first Hash, generate the next one, do stuff,... might be important for memory usage in case you have *lots* of combinations. (and yeah, i know, it is recursive and will already create x Hashes with 'x' the number of keys you use...) class GeneratingHash attr_accessor :constraints def initialize(constraints) @constraints = constraints end def recursive_each(keys,hash,&block) if keys.empty? yield hash else key = keys.pop @constraints[key].each { |val| my_hash = hash.clone my_hash[key]=val my_keys = keys.clone recursive_each(my_keys,my_hash,&block) } end end def each(&block) mykeys = @constraints.keys recursive_each(mykeys,Hash.new(),&block) end end For those who are interested, i also included the rest of the (relevant) code. (this is used in a program to perform a number of benchmarks based on a lot of different 'setups'(Profiles)) Comments on the code are welcome :) Ruben ============================== class Profile attr_accessor :size, :tidy, :new, :lo_freq, :hi_freq def initialize(values) @size = 22 unless @size = values["size"] @tidy = false unless @tidy = values["tidy"] @new = false unless @new = values["new"] if values["freq"] @lo_freq = values["freq"][0] @hi_freq = values["freq"][1] else @lo_freq = @hi_freq = 0 end end ... end class ProfileCollection attr_accessor :constraints def initialize(constraints) @constraints = constraints end def each(&block) gh = GeneratingHash.new(@constraints) gh.each { |hash| prof = Profile.new(hash) yield prof } end end ========================================= and those classes are used like this: constraints = {"size" => [22,23,24,25], "tidy" => [true,false], "new" => [true], "freq" => [[2,2],[2,4],[2,8]] } prof_collection = ProfileCollection.new(constraints) prof_collection.each { |profile| # do stuff with the profile }