30 Jul 2009, 11:25
Face_2_pragsmall

David Parry (7 posts)

Hi, Dave Thomas, and all others. Thanks for the Ruby Metaprogramming screencasts… my colleague and I have been finding them invaluable.

We have a problem that we have been trying to solve using metaprogramming techniques, but have so far failed. It currently works as a recursive algorithm, but it “smells” like something that would be far better implemented using blocks and lambdas. Here it is:

We are using Ruport to generate reports. These reports use x number of the query-able fields (state, country, year, department etc..), do a sum on an amount field and group the results. We want to be able to arbitrarily nest the results, which has lead us to a recursive algorithm.

Here’s a simplified example:

department = %w(sales research production)
years = %w(2005 2006 2007 2008 2009)
months = %w(January February March April May June July August September October November December)
product_type = %w(big little skinny)

def output_line(args)
 args = [args].flatten
 a_string = ""
 args.each {|a| a_string += "#{a} "}
 puts "#{a_string} amount = #{rand(100)}"
end

def process(arr)
 p2(arr.reverse, [])
end

def p2(arr, params = [])
 arr_clone = arr.clone
 top = arr_clone.pop
 p = params.clone
 if top.is_a?(Array)
   top.each do |a|
     new_params = [p, a].flatten
     p2(arr_clone, new_params)
   end
 else
   output_line(params)
 end
end

process([department, years, months])
puts "************"
process([years, department])
puts "************"
process([months, department, years, product_type])
puts "************"
process([department])
puts "************"

As I said, this smells like something that could be done using lambdas or Procs, but it’s got us a tad confused. Of course, as a recursive algorithm, perhaps this may well be a perfectly good implementation, if a little confusing.

Does anyone have any suggestions about how to approach this?

30 Jul 2009, 11:29
Face_2_pragsmall

David Parry (7 posts)

Here’s some sample output, btw:

sales 2005 January  amount = 70
sales 2005 April  amount = 39
sales 2005 July  amount = 13
sales 2005 October  amount = 44
sales 2006 January  amount = 57
sales 2006 April  amount = 30
sales 2006 July  amount = 55
sales 2006 October  amount = 56
sales 2007 January  amount = 54
sales 2007 April  amount = 98
sales 2007 July  amount = 14
sales 2007 October  amount = 53
production 2005 January  amount = 1
production 2005 April  amount = 99
production 2005 July  amount = 30
production 2005 October  amount = 69
production 2006 January  amount = 91
production 2006 April  amount = 19
production 2006 July  amount = 24
production 2006 October  amount = 10
production 2007 January  amount = 2
production 2007 April  amount = 19
production 2007 July  amount = 10
production 2007 October  amount = 94
************
2005 sales  amount = 17
2005 production  amount = 72
2006 sales  amount = 25
2006 production  amount = 63
2007 sales  amount = 16
2007 production  amount = 73
************
January sales 2005 big  amount = 94
January sales 2005 little  amount = 35
January sales 2006 big  amount = 14
January sales 2006 little  amount = 39
January sales 2007 big  amount = 57
January sales 2007 little  amount = 72
January production 2005 big  amount = 40
January production 2005 little  amount = 58
January production 2006 big  amount = 12
January production 2006 little  amount = 87
January production 2007 big  amount = 57
January production 2007 little  amount = 7
April sales 2005 big  amount = 60
April sales 2005 little  amount = 34
April sales 2006 big  amount = 80
April sales 2006 little  amount = 25
April sales 2007 big  amount = 20
April sales 2007 little  amount = 81
April production 2005 big  amount = 17
April production 2005 little  amount = 94
April production 2006 big  amount = 67
April production 2006 little  amount = 86
April production 2007 big  amount = 62
April production 2007 little  amount = 76
July sales 2005 big  amount = 55
July sales 2005 little  amount = 52
July sales 2006 big  amount = 79
July sales 2006 little  amount = 4
July sales 2007 big  amount = 60
July sales 2007 little  amount = 79
July production 2005 big  amount = 20
July production 2005 little  amount = 26
July production 2006 big  amount = 20
July production 2006 little  amount = 14
July production 2007 big  amount = 16
July production 2007 little  amount = 47
October sales 2005 big  amount = 6
October sales 2005 little  amount = 86
October sales 2006 big  amount = 55
October sales 2006 little  amount = 7
October sales 2007 big  amount = 4
October sales 2007 little  amount = 48
October production 2005 big  amount = 59
October production 2005 little  amount = 76
October production 2006 big  amount = 86
October production 2006 little  amount = 6
October production 2007 big  amount = 2
October production 2007 little  amount = 64
************
sales  amount = 43
production  amount = 4
************
30 Jul 2009, 17:53
Dave_gnome_head_isolated_pragsmall

Dave Thomas (337 posts)

I’m not sure it’s a metaprogramming issue, but you can use some Ruby method-passing semantics to tidy things up a bit:

department = %w(sales research production)
years = %w(2005 2006 2007 2008 2009)
months = %w(January February March April May June July August September October November December)
product_type = %w(big little skinny)
    
def really_process(result, first=[], *rest)
  if first.empty?
    puts result.join(" ")
  else
    first.each do |item|
      result << item      
      really_process(result, *rest)
      result.pop
    end
  end
end

def process(*categories)
    really_process([], *categories)
end

process(department, years, months)
puts "************" 
process(years, department)
puts "************" 
process(months, department, years, product_type)
puts "************" 
process(department)
puts "************" 
31 Jul 2009, 00:31
Face_2_pragsmall

David Parry (7 posts)

Nice! I will try and use this approach in my production code. Although, I’m still thinking this could be done with blocks or lambdas.

What I imagine is some how creating an “iterator” of some sort, for each of the arrays, then say “iterator this one with this one inside of it.”

I’m still experimenting, so if I come up with something I’ll post it…

  You must be logged in to comment