dynamic loop in a hash

I'm trying to create a dynamic loop within the hash @data below and can't really seem to figure it out. The array within the hash has to be dynamic i:e the day number has to be generated by a loop either while or until and the name of the product and number are dynamic as well. I'll try to explain in the loop below

@numdeployed comes from a table in the db

i = 0 until i == 12   i.day.ago.to_date => { :coder=>@numdeployed, :imedidata=>@numdeployed, :balance=>@numdeployed, :CTMS=>@numdeployed },   i += 1 end

Original Data Hash

@data = {       1.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },       2.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },       3.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },       4.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },       5.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },       6.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },       7.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },       8.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },       9.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },      10.day.ago.to_date => { :coder=>200, :imedidata=>190, :balance=>70, :CTMS=>90 },       11.day.ago.to_date => { :coder=>190, :imedidata=>200, :balance=>200, :CTMS=>150 },       12.day.ago.to_date => { :coder=>100, :imedidata=>400, :balance=>100, :CTMS=>100 }

}

hope someone can help. Thanks

I'm trying to create a dynamic loop within the hash @data below and can't really seem to figure it out. The array within the hash has to be dynamic i:e the day number has to be generated by a loop either while or until and the name of the product and number are dynamic as well. I'll try to explain in the loop below

@numdeployed comes from a table in the db

i = 0 until i == 12   i.day.ago.to_date => { :coder=>@numdeployed, :imedidata=>@numdeployed, :balance=>@numdeployed, :CTMS=>@numdeployed },   i += 1 end

Original Data Hash

@data = {       1.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },

How can you have a Date object as the key to a hash? Show us for an actual date what you want to see here.

This should work:

items = 12.times.map { [i.day.ago.to_date, { :coder=>@numdeployed, :imedidata=>@numdeployed, :balance=>@numdeployed, :CTMS=>@numdeployed } ] }.flatten result = Hash[*items]

Daniel

Colin Law wrote in post #1071227:

Daniel Mantilla wrote in post #1071237:

This should work:

items = 12.times.map { [i.day.ago.to_date, { :coder=>@numdeployed, :imedidata=>@numdeployed, :balance=>@numdeployed, :CTMS=>@numdeployed } ] }.flatten result = Hash[*items]

Daniel

Thanks Daniel, I will try this.

Sam Ginko wrote in post #1071240:

Colin Law wrote in post #1071227:

  i.day.ago.to_date => { :coder=>@numdeployed, :imedidata=>@numdeployed, :balance=>@numdeployed, :CTMS=>@numdeployed },   i += 1 end

Original Data Hash

@data = {       1.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },

How can you have a Date object as the key to a hash? Show us for an actual date what you want to see here.

the date object has to be there. That helps to build the javascript that creates the graphical timeline.

Daniel, I tried your suggestion but it did not work. I"m not sure if you can look at the actual plugin code. Maybe this will give you an idea of what I'm trying to do. Thanks again.

########################## original controller code. code I'm trying to replace with what you have written

@data = {      1.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },      2.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },      3.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },      4.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },      5.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },      6.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },      7.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },      8.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },      9.day.ago.to_date => { :coder=>10, :imedidata=>40, :balance=>10, :CTMS=>40 },      10.day.ago.to_date => { :coder=>200, :imedidata=>190, :balance=>70, :CTMS=>90 },      11.day.ago.to_date => { :coder=>190, :imedidata=>200, :balance=>200, :CTMS=>150 },      12.day.ago.to_date => { :coder=>100, :imedidata=>400, :balance=>100, :CTMS=>100 }      }

################################ View code

<%= inline_annotated_timeline @data, "900", "300", 'div_id_to_create' %>

####################### plugin code ######################

## Sample generated Javascript

# <script type="text/javascript" src="http://www.google.com/jsapi&quot;&gt;&lt;/script&gt; # <script type="text/javascript"> # google.load("visualization", "1", {packages:["annotatedtimeline"]}); # google.setOnLoadCallback(drawChart);function drawChart(){var data = new google.visualization.DataTable(); # data.addColumn('date', 'Date'); # data.addColumn('number', 'All'); # data.addColumn('number', 'Invalid'); # data.addColumn('number', 'Opt Out'); # data.addRows(166); # data.setValue(0, 0, new Date(2008, 6, 6)); # data.setValue(0, 1, 216); # data.setValue(0, 2, 27); # data.setValue(0, 3, 10); # data.setValue(1, 0, new Date(2008, 6, 7)); # data.setValue(1, 1, 327); # data.setValue(1, 2, 45); # data.setValue(1, 3, 14);

require "rubygems" require 'active_support'

module AnnotatedTimeline

    #this version will automatically create a div inline for you.     #note that if it is in the middle of the page, the javascript will try to execute before the page load completes   def inline_annotated_timeline(daily_counts_by_type, width = 750, height = 300, div_id_to_create = 'graph', options = {})     html = "<script type=\"text/javascript\" src=\"http://www.google.com/jsapi\\&quot;&gt;&lt;/script&gt;&quot;\.html\_safe     html << annotated_timeline(daily_counts_by_type, div_id_to_create, options)     html << "<div id=\"#{div_id_to_create}\" style=\"width: #{width}px\; height: #{height}px\;\"></div>".html_safe   end

  #you must create a div on your page and pass in the div id   def annotated_timeline(daily_counts_by_type, div_id = 'graph', options = {})     @time_zone = options.delete(:timeZone)     daily_graph = is_daily?(daily_counts_by_type)     google_graph_html = google_graph_data(daily_counts_by_type, daily_graph, options)

    google_options = format_options_for_javascript(options, daily_graph)     data_args = google_options.any? ? "data, {#{google_options.join(", ")}}" : "data"

    html = "<script type=\"text/javascript\">     google.load(\"visualization\", \"1\", {packages:[\"annotatedtimeline\"]});     google.setOnLoadCallback(drawChart);     function drawChart(){       var data = new google.visualization.DataTable();       #{google_graph_html}       var chart = new google.visualization.AnnotatedTimeLine(document.getElementById(\'#{div_id}\'));       chart.draw(#{data_args});     }     </script>".html_safe   end

private   def ruby_time_to_js_date(date)     "new Date(#{date.year}, #{date.month-1}, #{date.day})"   end

  def ruby_time_to_js_time(time)     time = time.to_time if !time.is_a?(Time)     if @time_zone && time.respond_to?(:in_time_zone)       time = time.in_time_zone(@time_zone)     end     "new Date(#{time.year}, #{time.month-1}, #{time.day}, #{time.hour}, #{time.min})"   end

  def ruby_hash_to_js_hash(hash)     hash.map{|key,val| "#{key}: #{val}"}   end

  def format_options_for_javascript(options, daily_graph=true)    valid_options = {}    options.each do |k,v|      valid_options[k] = case v        when String, Array then v.inspect #this turns 'red' into '\"red\"' for strings and %w[red blue] into [\"red\", \"blue\"] for arrays        when Date,Time then (daily_graph) ? ruby_time_to_js_date(v) : ruby_time_to_js_time(v)        else v #leave bools and numbers as they are       end    end

    # a hash containing annotations is passed into the ruby options hash, keyed as :annotations. this hash     # doesn't get sent to the javascript function - javascript takes the annotations as data points. the js     # function does, however, require the boolean :displayAnnotations     if (options[:annotations] && options[:annotations].any?)       valid_options[:displayAnnotations] = true     end     valid_options.reject!{|k,v| k == :annotations || v == nil}

    ruby_hash_to_js_hash valid_options   end

  def is_daily?(daily_counts_by_type={})     return true if (daily_counts_by_type.keys.first.is_a?(Date))     return true if (daily_counts_by_type.length < 2)

    sorted_keys = daily_counts_by_type.keys.sort     (sorted_keys[1] - sorted_keys[0]) > 2.hours   end

  def google_graph_data(daily_counts_by_type, daily_graph, options)     categories =     num = 0     html = ""     # Offset used to keep track of nested annotations.     index_offset = 0

    #set up columns and assign them each an index     if daily_graph       html << "data.addColumn('date', 'Date'); \n"     else       html << "data.addColumn('datetime', 'Date'); \n"     end     types( daily_counts_by_type ).each do |type|       html<<"data.addColumn('number', '#{type.titleize}');\n"       categories << type.to_sym

      if options[:annotations] && options[:annotations].keys.include?(type.to_sym)         html<<"data.addColumn('string', '#{type.titleize}_annotation_title');\n"         categories << "#{type}_annotation_title".to_sym

        html<<"data.addColumn('string', '#{type.titleize}_annotation_text');\n"         categories << "#{type}_annotation_text".to_sym

        options[:annotations][type.to_sym].each do |date, array|           if not daily_counts_by_type.key?(date)             daily_counts_by_type[date] = {}           end

          # We'll need additional date fields only if we have more than one annotation.           index_offset += (array.size-1)

          # Preserve multiple annotations.           daily_counts_by_type[date]["#{type}_annotation_title".to_sym] = array         end       end     end

    #The script expects a constant telling it how many rows we're going to add     html<<"data.addRows(#{(daily_counts_by_type.size+index_offset)});\n"

    html<<add_data_points(daily_counts_by_type, categories, daily_graph)     html   end

  # Converts this:   # { :Date1=>{:type1=>9, :type2=>9},   # :Date2=>{:type1=>9, :type3=>9} }   # into   # ['type1', 'type2', 'type3']   # We can't just take the keys of the first item because not every date has every category   def types( daily_counts_by_type )     daily_counts_by_type.values.inject({}){|a,b|a.merge(b)}.stringify_keys.keys.sort   end

  def add_data_points(daily_counts_by_type, categories, daily_graph)     html = ""     total_count = 0      # Offset used to keep track of nested annotations.     index_offset = 0

    #sort by date     daily_counts_by_type.sort{|a,b| a[0]<=>b[0]}.each_with_index do

obj, index|

      date, type_and_count = obj       js_date = (daily_graph) ? ruby_time_to_js_date(date) : ruby_time_to_js_time(date)       html<<"data.setValue(#{(index+index_offset)}, 0, #{js_date});\n"

      #now, on a particular date, go through columns       categories.each_with_index do |category, idx2|         value = type_and_count[category]         if value && category.to_s.include?("_annotation_")           first_iteration = true           value.each do |v|             if (!first_iteration)               index_offset += 1               html<<"data.setValue(#{(index+index_offset)}, 0, #{js_date});\n"             end             html<<"data.setValue(#{(index+index_offset)}, #{idx2+1}, \"#{v[0]}\");\n" if v[0]             # XXX: We assume the _text index is right after the _title index.             html<<"data.setValue(#{(index+index_offset)}, #{idx2+2}, \"#{v[1]}\");\n" if v[1]             first_iteration = false           end         elsif value           total_count = total_count + value.to_i           html<<"data.setValue(#{(index+index_offset)}, #{idx2+1}, #{value});\n" if value         end       end     end     (total_count > 0) ? html : ""   end

end