【问题标题】:How should I objectize this data in ruby?我应该如何在 ruby​​ 中客观化这些数据?
【发布时间】:2012-11-16 20:20:12
【问题描述】:

我将以下文本放入一些 Ruby 对象中,以便我可以将它们写入数据库以在 Rails 应用程序中使用。这些数据是波浪预报模型的一些输出,该模型显示了海洋中特定点的海洋膨胀。第一列是日期和时间,然后是组合膨胀(对此不感兴趣),然后是单个膨胀,在任何一个小时内可能出现 1 到 6 个膨胀。

 +-------+-----------+-----------------+-----------------+-----------------+
 | day & |  Hst  n x |    Hs   Tp  dir |    Hs   Tp  dir |    Hs   Tp  dir |
 |  hour |  (m)  - - |    (m)  (s) (d) |    (m)  (s) (d) |    (m)  (s) (d) |
 +-------+-----------+-----------------+-----------------+-----------------+
 | 15  3 | 0.94  3   |   0.74  4.4  69 |   0.43 10.6 186 |   0.39  4.8 351 |
 | 15  4 | 0.90  3   |   0.71  4.2  68 |   0.43 10.7 186 |   0.34  4.7 347 |
 | 15  5 | 0.85  3   |   0.65  4.1  72 |   0.42 10.7 186 |   0.35  4.4 351 |
 | 15  6 | 0.81  3   |   0.61  4.2  72 |   0.42 10.7 186 |   0.32  4.5 350 |
 | 15  7 | 0.77  2   |                 |   0.41 10.8 186 |                 |
 | 15  8 | 0.73  2   |                 |   0.40 10.8 186 |                 |
 | 15  9 | 0.70  3   |   0.51  3.8  74 |   0.40 10.7 187 |   0.26  4.1 350 |
 | 15 10 | 0.67  3   |   0.49  3.8  73 |   0.39 10.7 187 |   0.24  4.2 349 |
 | 15 11 | 0.65  3   |   0.47  3.7  73 |   0.38 10.7 186 |   0.23  4.1 352 |
 | 15 12 | 0.63  2   |                 |   0.37 10.7 187 |                 |
 | 15 13 | 0.63  2   |                 |   0.35 10.6 187 |                 |

我对日期、涌浪数量以及每次涌浪的信息感兴趣。我所追求的是一个包含天/小时作为键的对象,并且还包含每个膨胀的单独数据。涌浪的数量每小时会有所不同。如果我加载行:

| 15 11 | 0.65  3   |   0.47  3.7  73 |   0.38 10.7 186 |   0.23  4.1 352 |

我想通过如下调用从对象中获取信息:

@forecast.date              #=> 15:11
@forecast.numswells         #=> 3 for the total swells present on that date 
@forecast.swell.1.height    #=> 0.47
@forecast.swell.1.direction #=> 73
@forecast.swell.3           #=> a swell object with all info in it for swell 3

我认为我需要的是一个具有可变长度存储其他对象的对象。那可能吗?关于我应该阅读什么的任何指示?

【问题讨论】:

  • 输入数据是什么? XML? JSON?纯文本?
  • 我们可以猜到 Hs 是高度,dir 是方向,但是 Tp 是什么?您的问题对该部分不清楚。

标签: ruby-on-rails ruby oop object-oriented-analysis


【解决方案1】:

这是一个解析该文本行的对象:

class DayOnTheWater

  Swell = Struct.new(:hs, :tp, :d)

  attr_reader :day, :hour

  def initialize data_line
    data = data_line.split(/[|\s]/).delete_if {|col| col.empty?}
    @day = data.shift
    @hour = data.shift
    2.times { data.shift } # remove combined swell data
    @swells = data.each_slice(3).map { |hs, tp, d| Swell.new(hs.to_f, tp.to_f, d.to_i) }
  end

  def swells
    @swells.to_enum
  end

end

example = '| 15 11 | 0.65  3   |   0.47  3.7  73 |   0.38 10.7 186 |   0.23  4.1 352 |'

object =  DayOnTheWater.new(example)
puts "day: #{object.day}"
puts "hour: #{object.hour}"
puts "\nSWELL DATA"
object.swells.each { |swell| puts swell.inspect }
puts "\nExample statistic:"
puts "Max Hs: #{object.swells.max { |a,b| a.hs <=> b.hs }}"

输出:

day: 15
hour: 11

SWELL DATA
#<struct DayOnTheWater::Swell hs=0.47, tp=3.7, d=73>
#<struct DayOnTheWater::Swell hs=0.38, tp=10.7, d=186>
#<struct DayOnTheWater::Swell hs=0.23, tp=4.1, d=352>

Example statistic:
Max Hs: #<struct DayOnTheWater::Swell hs=0.47, tp=3.7, d=73>

结构体适用于简单的值对象。它们有缺点,但输入起来很快。对于公开事物列表的对象,我总是返回一个 Enumerable 而从不返回底层数据对象(这里的 swells 方法)。这很重要,因为 Enumerables 是不可变的(只读);这可以防止其他对象更改数据。

【讨论】:

  • 好方法。您可能需要添加一条说明,说明您建议如何将 Struct 数据持久化到数据库中,因为这是他所要求的大部分内容。
  • 持久化数据是一个相当开放的事情——ActiveRecord? SQL?平面文件?商店?雷迪斯?蒙哥?数据必须被索引吗?
  • 好吧,通过他的问题来猜测,“我是一个红宝石菜鸟”和“所以我可以把它放在 Rails 应用程序中”可能意味着最简单的 ActiveRecord 实现会非常有帮助。不管怎样,你都得到了我的支持 :)
  • 好吧,我不会为你做你的工作;)。不过,这里有一个大纲:对于 activerecord,可能会将 Swell 分解为它自己的模型对象。使 DayOnTheWater 和 Swell 继承 ActiveRecord::Base。 DayOnTheWater 将使用 has_many,而 Swell 可能会使用 belongs_to
  • 顺便说一句,你也可以写.delete_if(&amp;:empty?) ;-)
【解决方案2】:

一些建议:

  • 对于一组事物,您最好使用 swells 的复数形式而不是 swell
  • 对于不同长度的东西,Array 是最合适的。当引用 Array 的特定元素时,您应该使用 [] 方法而不是 12 等方法(这可能首先是不可能的)。在这种情况下,请注意您应该从0 开始,而不是1
  • 您可以通过对膨胀应用length 方法轻松获取膨胀数量。你不应该有一个特定的方法numswells,除非它会被特别频繁地使用。

我会这样做:

data =
"| 15 11 | 0.65  3   |   0.47  3.7  73 |   0.38 10.7 186 |   0.23  4.1 352 |"

class Forecast
  attr_reader :date, :swells
  def initialize string
    _, date, _, swells = string.split("|", 4)
    @date = date.scan(/\d+/).join(":")
    @swells = swells.scan(/[^\|]+/).select{|s| s =~ /\S/}.map{|s| Swell.new(s)}
  end
end

class Swell
  attr_reader :height, :tp, :direction
  def initialize string
    @height, @tp, @direction = string.split(/\s+/).drop(1).map(&:to_f)
  end
end

@forecast = Forecast.new(data)
p @forecast.date
p @forecast.swells.length
p @forecast.swells[0].height
p @forecast.swells[0].direction
p @forecast.swells[2]

#=> "15:11"
#=> 3
#=> 0.47
#=> 73.0
#=> #<Swell:0x000000016401d0 @height=0.23, @tp=4.1, @direction=352.0>

【讨论】:

  • 不错的答案。 _, date, _, swells = string.split("|", 4) 在代码中做了什么?
  • 它将string最多拆分为"|"四个部分,并丢弃第一个(空字符串)和第三个(组合膨胀)部分,将date分配给第二个(天&小时)和swells 第四部分(所有其余部分)。
猜你喜欢
  • 2011-04-13
  • 2018-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-05
  • 1970-01-01
  • 1970-01-01
  • 2022-07-19
相关资源
最近更新 更多