Ruby-on-rails – Ruby model with an array as an attribute

arrayshashmodelingrubyruby-on-rails

I am currently trying to implement a model to simplify graphical chart creation. However, one of the attributes must be an array of attributes. For example:

"Chart_Series" has a "name" which would be a string and a data field which would be separated by dates which is an array of arrays [[Date1, Value1],[Date2,Value2],[Date3,Value3].

The purpose is to create an array of "Chart_Series" so as to call upon something like:

for series in @chart_series
  series.name 
     for data in series.data
      data.[Date, Value]
     end    
end

which would do something along the lines of:

  Name1
      Date1, Value1
      Date2, Value 2,
      Date3, Value 3,
      Date4, Value 4,
  Name2 
      Date1, Value 1,
      Date2, Value 2,
      Date3, Value 3,
      Date4, Value 4,

This is not exactly the code desired.. I am interested in simply generating the model which could do something like this. Any help is appreciated

Best Answer

I can see two initial approaches, namely define a class to represent your key,value pair or just use a hash to represent each data item. The advantage of a separate class is that you can extend it in the future, if for example you wanted to provide the exact value in a chart where you were rounding to the nearest 100k.

The following code shows three classes which together will do what you want

class Chart

  attr_accessor :title, :series

  def initialize(title = nil, series = [])
    @title, @series = title, series
  end

  def show
    puts title
    @series.each do |ser|
      puts "\t#{ser.legend} (#{ser.units})"
      ser.data.each do |item|
        puts "\t\t#{item}"
      end
    end
  end

end

class Series

  attr_accessor :legend, :units, :data

  def initialize(legend = nil, units = nil, data = [])
    @legend, @units, @data = legend, units, data
  end

end

class DataItem
  attr_accessor :key, :value

  def initialize(key, value)
    @key, @value = key, value
  end

  def to_s
    "#{key}, #{value}"
  end

end

Running this as follows :-

c = Chart.new("Sweet sales by Quarter")
c.series << Series.new("Bon-Bons", "£000", 
  [ DataItem.new("Q1", 220), 
    DataItem.new("Q2", 280), 
    DataItem.new("Q3", 123), 
    DataItem.new("Q4", 200)]
)
c.series << Series.new("Humbugs", "£000",  
  [ DataItem.new("Q1", 213), 
    DataItem.new("Q2", 254), 
    DataItem.new("Q3", 189), 
    DataItem.new("Q4", 221)]
)

c.show

Produces the following output

Sweet sales by Quarter
    Bon-Bons (£000)
        Q1, 220
        Q2, 280
        Q3, 123
        Q4, 200
    Humbugs (£000)
        Q1, 213
        Q2, 254
        Q3, 189
        Q4, 221

If you wanted to take the Hash approach then you would no longer need the DataItem class and you could instantiate a new Series with code like this

c = Chart.new("Sweet sales by Quarter")
c.series << Series.new("Bon-Bons", "£000", 
   [ { "Q1" => 220}, {"Q2" => 280}, {"Q3" => 123}, {"Q4" => 200}]
)

The show method of Chart would then look like this

  def show
    puts title
    @series.each do |ser|
      puts "\t#{ser.legend} (#{ser.units})"
      ser.data.each do |item|
        item.each_pair {|key, value| puts "\t\t#{key}, #{value}" }
      end
    end
  end