質問

I have the following text to put into some Ruby objects so that I can write them into a DB to be used in a rails app. The data is some output from a wave forecast model, which shows ocean swells at a particular point in the ocean. The first column is the day and hour, then combined swell (not interested in this), followed by individual swells which can vary from 1 to 6 swell present in any one hour.

 +-------+-----------+-----------------+-----------------+-----------------+
 | 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 |                 |

I'm interested in the date, number of swells, and info about each swell. What I'm after is an object that contains the day/hour as the key, and also contains the individual data for each swell. The number of swells will vary for each hour. If I loaded line:

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

I'd like to get info out of the object with calls like:

@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

I think what I need is an object which has a variable length store of other objects. Is that possible? Any pointers as to what I should be reading up on?

役に立ちましたか?

解決 2

Some suggestions:

  • You should better use swells in plural than swell for a set of things.
  • For things of varying length, Array is the best fit. When referring to a particular element of an Array, you should use the [] method instead of methods like 1, 2, etc. (Probably that is not possible in the first place). In that case, note that you should start from 0, not 1.
  • You can easily get the number of swells by applying the length method on the swells. You should not have a specific method numswells for that unless it will be used particularly frequently.

I would do something like this:

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>

他のヒント

Here is an object that parses a line of that text:

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 }}"

Output:

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>

Structs are good for simple value objects. They have downsides, but they are quick to type in. For objects that expose lists of things, I always return an Enumerable and never the underlying data object (the swells method here). This is important because Enumerables are immutable (read-only); this prevents other objects from changing the data.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top