2

コンマ区切りの文字列と型を指定する別の配列を指定して、異なる型の配列を作成するにはどうすればよいですか?


から取得した CSV 入力を解析することによりstdin、列ヘッダーSymbolの配列が得られます。

cols = [:IndexSymbol, :PriceStatus, :UpdateExchange, :Last]

そして生の入力の行:

raw = "$JX.T.CA,Open,T,933.36T 11:10:00.000"

の各要素がの対応する要素によって識別される型であるcells、入力から配列を作成したいと思います。これを行う慣用的なRuby-shの方法は何ですか?rawcellscols


私はこれを試しましたが、うまくいきますが、実際には正しくありません。

1) まず、カプセル化する必要がある各タイプのクラスを定義します。

class Sku
  attr_accessor :mRoot, :mExch,, :mCountry
  def initialize(root, exch, country)
    @mRoot = root
    @mExch = exch
    @mCountry = country
  end
end

class Price
  attr_accessor :mPrice, :mExchange, :mTime
  def initialize(price, exchange, time)
    @mPrice = price
    @mExchange = exchange
    @mTime = time
  end
end

2) 次に、変換する必要がある一意の列タイプごとに変換関数を定義します。

def to_sku(raw)
  raw.match('(\w+)\.(\w{0,1})\.(\w{,2})') { |m| Sku.new(m[1], m[2], m[3])}
end

def to_price(raw)

end

3) 入力から文字列の配列を作成します。

cells = raw.split(",")

4) 最後にcells、対応する列ヘッダーによって指定された型を構築することにより、インプレースの各要素を変更します。

cells.each_index do |i|
    cells[i] = case cols[i]
        when :IndexSymbol
            to_sku(cells[i])
        when :PriceStatus
            cells[i].split(";").collect {|st| st.to_sym}
        when :UpdateExchange
            cells[i]
        when :Last
            cells[i].match('(\d*\.*\d*)(\w?) (\d{1,2}:\d{2}:\d{2}\.\d{3})') { |m| Price.new(m[1], m[2], m[3])}
        else
            puts "Unhandled column type (#{cols[i]}) from input string: \n#{cols}\n#{raw}"
            exit -1
    end
end

正しくないと感じる部分はステップ 3 と 4 です。これをより Ruby 風にするにはどうすればよいでしょうか? 私は、私の想像の中にのみ存在する、このようなある種の超簡潔な方法を想像していました:

cells = raw.split_using_convertor(",")
4

4 に答える 4

2

#zip#map、および分解代入を使用して、4 番目のステップをより簡単にすることができます。

cells = cells.zip(cols).map do |cell, col|
    case col
    when :IndexSymbol
        to_sku(cell)
    when :PriceStatus
        cell.split(";").collect {|st| st.to_sym}
    when :UpdateExchange
        cell
    when :Last
        cell.match('(\d*\.*\d*)(\w?) (\d{1,2}:\d{2}:\d{2}\.\d{3})') { |m| Price.new(m[1], m[2], m[3])}
    else
        puts "Unhandled column type (#{col}) from input string: \n#{cols}\n#{raw}"
        exit -1
    end
end

そのステップを分割と組み合わせることはお勧めしません.CSVの行を解析することは、それ自体のステップになるほど複雑であるためです. CSV を解析する方法については、私のコメントを参照してください。

于 2013-08-19T15:23:18.347 に答える
2

さまざまな型を基本クラスから継承させ、検索知識をその基本クラスに入れることができます。次に、生の文字列から自分自身を初期化する方法を各クラスに知らせることができます。

class Header
  @@lookup = {}

  def self.symbol(*syms)
    syms.each{|sym| @@lookup[sym] = self}
  end

  def self.lookup(sym)
    @@lookup[sym]
  end
end

class Sku < Header
  symbol :IndexSymbol
  attr_accessor :mRoot, :mExch, :mCountry

  def initialize(root, exch, country)
    @mRoot = root
    @mExch = exch
    @mCountry = country
  end

  def to_s
    "@#{mRoot}-#{mExch}-#{mCountry}"
  end

  def self.from_raw(str)
    str.match('(\w+)\.(\w{0,1})\.(\w{,2})') { |m| new(m[1], m[2], m[3])}
  end
end

class Price < Header
  symbol :Last, :Bid
  attr_accessor :mPrice, :mExchange, :mTime

  def initialize(price, exchange, time)
    @mPrice = price
    @mExchange = exchange
    @mTime = Time.new(time)
  end

  def to_s
    "$#{mPrice}-#{mExchange}-#{mTime}"
  end

  def self.from_raw(raw)
    raw.match('(\d*\.*\d*)(\w?) (\d{1,2}:\d{2}:\d{2}\.\d{3})') { |m| new(m[1], m[2], m[3])}
  end
end

class SymbolList
  symbol :PriceStatus
  attr_accessor :mSymbols

  def initialize(symbols)
    @mSymbols = symbols
  end

  def self.from_raw(str)
    new(str.split(";").map(&:to_sym))
  end

  def to_s
    mSymbols.to_s
  end
end

class ExchangeIdentifier
  symbol :UpdateExchange
  attr_accessor :mExch

  def initialize(exch)
    @mExch = exch
  end

  def self.from_raw(raw)
    new(raw)
  end

  def to_s
    mExch
  end
end

次に、ステップ 4 を次のように置き換えることができます (CSV 解析は含まれません)。

cells.each_index.map do |i|
  Header.lookup(cols[i]).from_raw(cells[i])
end
于 2013-08-19T15:27:06.270 に答える
1

Ruby のCSV ライブラリには、この種のものを直接サポートする機能が含まれています (実際の構文解析の処理も改善されています)。

変換を行うを提供し、procそれをオプションとして に渡す必要がありますCSV.parse

converter = proc do |field, info|
  case info.header.strip # in case you have spaces after your commas
  when "IndexSymbol"
      field.match('(\w+)\.(\w{0,1})\.(\w{,2})') { |m| Sku.new(m[1], m[2], m[3])}
  when "PriceStatus"
      field.split(";").collect {|st| st.to_sym}
  when "UpdateExchange"
      field
  when "Last"
      field.match('(\d*\.*\d*)(\w?) (\d{1,2}:\d{2}:\d{2}\.\d{3})') { |m| Price.new(m[1], m[2], m[3])}
  end
end

次に、それをほぼ直接、必要な形式に解析できます。

c =  CSV.parse(s, :headers => true, :converters => converter).by_row!.map do |row|
  row.map { |_, field| f }  #we only want the field now, not the header
end
于 2013-08-19T15:40:20.300 に答える