Ruby model with an array as an attribute

2019-07-07 07:51发布

问题:

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

回答1:

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


回答2:

Assuming you intend to use a relational database backend with ActiveRecord then I beleive you need (at least) two models. Something like this:

class Chart < ActiveRecord::Base
  has_many :data_points
end

class DataPoint < ActiveRecord::Base
  belongs_to :chart
end

Your Chart model would have the name attribute and the DataPoint model would have the date and value attributes.

Then you can reference your data points, as an association, like this:

@chart.data_points

so it looks like an array attribute, even though it is an association with another model



回答3:

You try something like this

Say you have a class called User and you need to generate its attributes dynamically, you can do this like this,

class User User.class_exec{ %w[name age address].each{|i| attr_accessor i
} } end

this will create a User class with attributes name, age and address

and you can use this as

a = User.new a.name = "sameera" a.age = "29" a.address = "home town" puts a.inspect

hope this helps

cheers sameera