番号文字列をインクリメントする

"Z10-036K"みたいな文字列があったとして、"Z10-999K"の次は"Z11-000K"になってほしいよねということで書いてみました。

ソース(というほどのものではない)

Stringクラスに defineするので既存のメソッドをオーバーライトしないように、なるべくダサい名前にしてみました。(ここが一番時間かかったところ)

class String
  def to_numstr
    self.unpack("C*").map{|e|e.chr=~/\d/&&$&}.to_s
  end

  def to_numstr_next
    s = (self.to_numstr.to_i + 1).to_s
    return self + s   if s == "1"
    ret = ""
    self.size.times do |i|
      if ?0 <= self[i] && self[i] <= ?9
        ret += s[0..0]
        s = s[1..-1] || ""
      else
        ret += self[i..i]
      end
    end
    ret + s
  end
end

to_numstr

文字列の中から数字だけをピックアップして文字列として返します。数字が含まれないと""が返ります。

"Z10-999K".to_numstr  #=>  "10999"
"Z-K".to_numstr       #=>  ""

to_numstr_next

文字列の数字部分をインクリメントして返します。数字のMAXまでいって桁が増えたらフォーマットがずれます。数字が含まれない場合は文字列に"1"を付けて返します。

"Z10-999K".to_numstr_next   #=>  "Z11-000K"
"Z-K".to_numstr_next        #=>  "Z-K1"
"Z99-999K".to_numstr_next   #=>  "Z10-000K0"
"Z10-000K1".to_numstr_next  #=>  "Z10-000K2"

まとめ(ではない)

会員番号などで無駄に頭にアルファベットがついているのがあって、コツコツ先頭から数字以外を取って、数字を処理してから戻したりしていたんですが、途中に入っているケースなんかが出てきて、「どういうコード設計しとるんじゃい!責任者出てこい!」という感じになったので、心の平穏を保つために書きました。