Rubyメモ

連続する同じ文字で文字列を分割

scan

str = "122333455"
str.scan(/((.)\2*)/).map(&:first)
#=> ["1", "22", "333", "4", "55"]

括弧が2つあるのは、内側の括弧のドットで1文字にマッチし、外側の括弧でその繰り返しをキャプチャするため。ドットにマッチした文字は \2 で参照する。scanの結果は、外側括弧の連続文字全体と、内側括弧の単文字の配列となる。

p str.scan(/((.)\2*)/
#=> [["1", "1"], ["22", "2"], ["333", "3"], ["4", "4"], ["55", "5"]]

How to split a string of repeated characters with uneven amounts? Ruby

chunk_while

chunk_whileを使い、前の文字と等しい場合を条件にすることで同じ文字が続く単位をグループ化することもできる。

 str.chars.chunk_while{|a,b|a==b}.to_a
=> [["1"], ["2", "2"], ["3", "3", "3"], ["4"], ["5", "5"]]

母音(aeiou)と子音で分ける。

v="aeiou"
str="throughout"
str.chars.chunk_while{|i,j| v.include?(i) == v.include?(j)}.to_a
=> [["t", "h", "r"], ["o", "u"], ["g", "h"], ["o", "u"], ["t"]]

chunk

chunkは要素を順にブロックで評価しその結果でグループ分けする。

[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5].chunk {|n|n.even?}.to_a
=> [[false, [3, 1]], [true, [4]], [false, [1, 5, 9]], [true, [2, 6]], [false, [5, 3, 5]]]

[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5].chunk {|n|n%3}.to_a
=> [[0, [3]], [1, [1, 4, 1]], [2, [5]], [0, [9]], [2, [2]], [0, [6]], [2, [5]], [0, [3]], [2, [5]]]

同じ文字の連続でグループ分けする場合は itself が使える。

str = "122333455"
str.chars.chunk(&:itself).to_a
=> [["1", ["1"]], ["2", ["2", "2"]], ["3", ["3", "3", "3"]], ["4", ["4"]], ["5", ["5", "5"]]]

str.chars.chunk(&:itself).map{|x|x.last.join}
=> ["1", "22", "333", "4", "55"]

同じ文字の出現数を数える

group_byを使う場合

str="ABBCCCAAECBB"
str.chars.group_by{|a|a}.to_a
=> [["A", ["A", "A", "A"]], ["B", ["B", "B", "B", "B"]], ["C", ["C", "C", "C", "C"]], ["E", ["E"]]]
> str.chars.group_by{|a|a}.map{|k,v| "#{k}=#{v.size}"}
=> ["A=3", "B=4", "C=4", "E=1"]

countを使う方がシンプル

> str.chars.uniq.map{|c| "#{c}=#{str.count(c)}"}
=> ["A=3", "B=4", "C=4", "E=1"]

配列の並んだ順で2つずつ取り出す

each_consを使う。

> a=[3,1,4,1,5,9]
irb(main):025:0> a.each_cons(2).to_a
=> [[3, 1], [1, 4], [4, 1], [1, 5], [5, 9]]

each_cons(2).map とすることができる。

浮動小数の桁数指定

f = 3.14159
f.round(2)  #=> 3.14
f.round(3)  #=> 3.142