前の行と同じであれば空欄になっているCSVテキストのセルを全部埋めたい


国、州/県、都市を定義した、こんなCSVテキストがあったとします。

[code]

    日本,東京,新宿
    ,,渋谷
    ,兵庫,神戸
    アメリカ,カリフォルニア,サンフランシスコ
    ,,ロサンゼルス
    
[/code]

データは予めソートされていて、国や州が前のデータと同じであれば空欄になっています。

このテキストを変換して、セルを全部埋めるプログラムを作りたいと思っています。  
すなわち、期待するのは以下のようなアウトプットです。

[code]

    日本,東京,新宿
    日本,東京,渋谷
    日本,兵庫,神戸
    アメリカ,カリフォルニア,サンフランシスコ
    アメリカ,カリフォルニア,ロサンゼルス
    
[/code]

とりあえず僕の方で愚直にこんなロジックを考えたんですが、ブロックの外で定義する`current_country`、`current_state`や、`if
country.size > 0 ... end` がDRYになってない感じが自分でもちょっとしっくりきません。

[code]

      def fill_blank(csv_text)
        current_country, current_state = nil, nil
        csv_text.lines.map { |line|
          country, state, city = line.chomp.split(',')
          if country.size > 0
            current_country = country
          end
          if state.size > 0
            current_state = state
          end
          [current_country, current_state, city].join(',')
        }.join("\n")
      end
    
[/code]

もうちょっと短くてわかりやすい、スマートなロジックは作れないでしょうか？  
Railsで動かすのでActiveSupportのメソッドを使うのもOKです。  
参考までにテストコード付きのサンプルコードを載せておきます。

[code]

    require 'test/unit'
    
    class FillBlankTest < Test::Unit::TestCase
      def fill_blank(csv_text)
        current_country, current_state = nil, nil
        csv_text.lines.map { |line|
          country, state, city = line.chomp.split(',')
          if country.size > 0
            current_country = country
          end
          if state.size > 0
            current_state = state
          end
          [current_country, current_state, city].join(',')
        }.join("\n")
      end
    
      def test_fill_blank
        input = <<-TEXT
    日本,東京,新宿
    ,,渋谷
    ,兵庫,神戸
    アメリカ,カリフォルニア,サンフランシスコ
    ,,ロサンゼルス
        TEXT
    
        output = <<-TEXT.chomp
    日本,東京,新宿
    日本,東京,渋谷
    日本,兵庫,神戸
    アメリカ,カリフォルニア,サンフランシスコ
    アメリカ,カリフォルニア,ロサンゼルス
        TEXT
    
        assert_equal output, fill_blank(input)
      end
    end
    
[/code]

みなさんのアイデアをお待ちしています。




前の行をまるごと保存して、文字列が空なら前の行の項目を使う方法を考えてみました。

[code]

    def fill_blank(csv_text)
      prev = []
    
      csv_text.each_line.map do |line|
        row = line.chomp.split ","
        prev = row.zip(prev).map{|a, b| a.empty? ? b : a }
        prev * ","
      end * "\n"
    end
    
[/code]

CSVライブラリを使えばもう少し短くなります。(空文字列はnilになります)

[code]

    require 'csv'
    
    def fill_blank(csv_text)
      prev = []
    
      CSV.parse(csv_text).map do |row|
        prev = row.zip(prev).map{|a, b| a || b }
        prev * ","
      end * "\n"
    end
    
[/code]




前の行をまるごと保存して、文字列が空なら前の行の項目を使う方法を考えてみました。

[code]

    def fill_blank(csv_text)
      prev = []
    
      csv_text.each_line.map do |line|
        row = line.chomp.split ","
        prev = row.zip(prev).map{|a, b| a.empty? ? b : a }
        prev * ","
      end * "\n"
    end
    
[/code]

CSVライブラリを使えばもう少し短くなります。(空文字列はnilになります)

[code]

    require 'csv'
    
    def fill_blank(csv_text)
      prev = []
    
      CSV.parse(csv_text).map do |row|
        prev = row.zip(prev).map{|a, b| a || b }
        prev * ","
      end * "\n"
    end
    
[/code]

