Excel列名変換問題とは
Excelって行名は番号なのに列名がAlphabetじゃないですか.
なので,1列目は'A',2列目は'B',みたいな変換がしたくなるわけです.(axlsx使っててなりました)
ぐぐったところどうやらこれをExcel列名変換問題というらしく,いろんな言語で実装してる人がいるみたいです.
おもしろそうなのでRubyで実装してみました.
なにはともあれTest Code
関数のIN/OUTがはっきりしていて内部状態を持たない純粋関数みたいなものなので,まずまっさきにTest Codeから整備しておくべきである,というわけで ↓ のようなTest Codeを作りました.(Input/Outputの組み合わせはぐぐりました)
def test_convert_col_num_to_alphabet
assert_raises { convert_col_num_to_alphabet(0) }
assert_equal('A', convert_col_num_to_alphabet(1))
assert_equal('Z', convert_col_num_to_alphabet(26))
assert_equal('AA', convert_col_num_to_alphabet(27))
assert_equal('AZ', convert_col_num_to_alphabet(52))
assert_equal('BA', convert_col_num_to_alphabet(53))
assert_equal('ZZ', convert_col_num_to_alphabet(702))
assert_equal('AAA', convert_col_num_to_alphabet(703))
end
def test_convert_alphabet_to_col_num
assert_raises { convert_alphabet_to_col_num('') }
assert_raises { convert_alphabet_to_col_num(nil) }
assert_equal(1, convert_alphabet_to_col_num('A'))
assert_equal(26, convert_alphabet_to_col_num('Z'))
assert_equal(27, convert_alphabet_to_col_num('AA'))
assert_equal(52, convert_alphabet_to_col_num('AZ'))
assert_equal(53, convert_alphabet_to_col_num('BA'))
assert_equal(702, convert_alphabet_to_col_num('ZZ'))
assert_equal(703, convert_alphabet_to_col_num('AAA'))
end
できあがった実装
できるかぎりシンプルにしたかったのですが ↓ のくらいのところでいったん満足しました.
ALPHABET_COUNT = 26
ALPHABET_CODE_OFFSET = 'A'.ord
def convert_col_num_to_alphabet(count)
raiase "count is 0 or minus" if count <= 0
ret = ''
loop {
count = count - 1
remainder = count % ALPHABET_COUNT
char = (ALPHABET_CODE_OFFSET + remainder).chr
ret = char << ret
count = count / ALPHABET_COUNT
break if count == 0
}
return ret
end
def convert_alphabet_to_col_num(alphabet)
raise "alphabet is nil" if alphabet.nil?
raise "alphabet is empty" if alphabet.empty?
ret = 0
size = alphabet.size
alphabet.each_char { |char|
ret += (char.ord - ALPHABET_CODE_OFFSET + 1) * ALPHABET_COUNT ** (size - 1)
size -= 1
}
return ret
end
とりあえずこれで上のTest CodeもPassしました.
文字を数字として扱うのに.ordと.chrというAPIを使っていますが,これを見つけられなかったらもっと長くなってたかも.
['', 'A', 'B', 'C', ... 'Z']
っていう配列作ってIndexでアクセスしたり,
' ABC...Z'
っていう文字列作ってIndexでアクセスしたりしてたかも.
(こっちのほうが直感的でわかりやすい,って話もあるか)
count = count - 1
の処理が直感的にわかりにくいのが悩みどころ.
やってみた感想
Algorithm考えるのは楽しいです.
昔XML Parserを自力実装したことがありましたがそれと同じ雰囲気.(車輪の再発明感も)
しかしこんなLogic,超汎用なんだからどっかのFrameworkにAPI実装されててもおかしくないですよね. ← たぶんある
---///