LoginSignup
1

More than 5 years have passed since last update.

エクセルで表示される座標をインデックスに変換する

Last updated at Posted at 2016-04-13

よくcsv形式で作成したファイルをjsonに変換したりするのですが、そのときにRubyでCSVをパースすると二次元配列となります。
この二次元配列から指定座標の値を取得するためにはindexを指定して取得する必要があります。

しかし、エクセルやオープンオフィス等で表示される'AA10'といった座標の値を取得するためには、'AA'が配列でいう何番に対応するのかかなりわかりづらく、数え間違えもしやすく面倒です。

そこで、'AA'を2次元配列のindexに置き換えるコードをRubyで書いてみました。

書いたコード

CODES = ('A'..'Z')
CODES_SIZE = CODES.to_a.size

def code2index(code)
  code.split('').reverse.map.with_index do |c, i|
    n = c.ord - CODES.first.ord
    n += 1 unless i == 0
    n *= CODES_SIZE ** i
    n
  end.inject(:+)
end

実際に実行してみるとこんな感じになります。

irb(main):001:0> code2index('A')
=> 0
irb(main):003:0> code2index('Z')
=> 25
irb(main):002:0> code2index('AA')
=> 26
irb(main):004:0> code2index('BA')
=> 52
irb(main):005:0> code2index('ZZ')
=> 701
irb(main):006:0> code2index('AAA')
=> 702

仕組みについて

一見すると単純な25進数に見えるのですが、実は二桁以上になると罠があります。
25進数として考えると下記のようになってしまいます。

code 25進数 10進数
A 0 0
B 1 1
Z 25 25
AA 0, 0 0
BA 1, 0 26
AAA 0, 0, 0 0

二桁目以降の扱いが単純な25進数となっていないため、'A'が0となってしまうと'A'と'AA'と'AAA'が同一のものとして扱われてしまいます。
そこで二桁目以降は、1を可算した形で表現することで対処します。

code 25進数 10進数
A 0 0
B 1 1
Z 25 25
AA 1, 0 26
BA 2, 0 52
AAA 1, 1, 0 702

これでやっと座標に対応したインデックスを取得することが出来ます。

ちなみに、逆変換はこちら。

def index2code(n)
  chars = ''
  codes = CODES.to_a
  i = 0

  begin
    n -= 1 unless i == 0
    chars << codes[n % CODES_SIZE]
    n = n / CODES_SIZE
    i += 1
  end while n > 0

  chars.reverse
end

最後に

実際のCSVの座標の値を取得したりするのに便利なので、Gemとして作ってみました.
ご自由にお使いください。

kei-p/CSVCoordinateAccessor

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1