Ruby で文字列の表示幅をもとめるおはなし

Ruby には文字列の表示幅を求めるメソッドがありません(ニッチ)。 要は ASCII 文字を1文字、非 ASCII 文字(= マルチバイト文字)を2文字としてカウントしたいんです(ニッチ)(ニッチ)。

こんな感じで求めてみる

"hogeふが".chars
=> ["h", "o", "g", "e", "", ""]
# String#chars で1文字ずつにばらす

["h", "o", "g", "e", "", ""].map { |c| c.ascii_only? ? 1 : 2 }
=> [1, 1, 1, 1, 2, 2]
# ASCII 文字を1文字、非 ASCII 文字を2文字としてカウント

[1, 1, 1, 1, 2, 2].inject(0, &:+)
=> 8
# みんなだいすき Enumerable#inject で合計を求める

できた

class String
  def width
    self.chars.map{ |c| c.ascii_only? ? 1 : 2 }.inject(0, &:+)
  end
end

"hogeふが".width
=> 8

だがしかし

ちょっと効率がわるい(each が2回まわる)

作戦変更

"hogeふが".chars.reject(&:ascii_only?)
=> ["", ""]
# Array#reject で ASCII 文字を取り除く(= 非 ASCII 文字を抽出)

"hogeふが".length + ["", ""].length
=> 8

できた(2回目)

class String
  def width
    self.length + self.chars.reject(&:ascii_only?).length
  end
end

"hogeふが".width
=> 8

まとめ

Enumerable#inject だいすき