よく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として作ってみました.
ご自由にお使いください。