おどるダメ人間

Web系の技術のこととか、そうじゃないこととか、思いついたことをメモする。

ちょっと便利なActiveSupportの標準クラス拡張(Array & Hash編)

RailsActiveSupportにはcore_extというのがあって、標準クラスを拡張して便利なメソッドを追加してます。 Railsを使ってると実は結構いろんなところでお世話になっていて、たまに素のrubyを書くと「あれ、これ使えないのか」と戸惑ったりしますね。

一度俯瞰で把握しとくのもいいかなと思ってまとめてみました。 といっても多すぎて途中で力尽きたので、とりあえずよく使われそうなArrayとHashについて、適当に便利そうなのを見繕ってます。

環境は、Rails 4.1.1、ruby 2.0.0

Array

参考: rails/activesupport/lib/active_support/core_ext/array at master · rails/rails

from と to

%w( a b c d ).from(0)  # => ["a", "b", "c", "d"]
%w( a b c d ).from(2)  # => ["c", "d"]

%w( a b c d ).to(0)  # => ["a"]
%w( a b c d ).to(2)  # => ["a", "b", "c"]

second 〜 fifth(+おまけ?)

firstは標準Arrayにもあるんですが、ActiveSupportでは2〜5個目までとれるみたいです。

%w( a b c d e ).second # => "b"
%w( a b c d e ).third  # => "c"
%w( a b c d e ).fourth # => "d"
%w( a b c d e ).fifth  # => "e"

ついでに、なぜか42個目もとれるという謎のforty_twoメソッドも…。

(1..42).to_a.forty_two # => 42

なぜ42なのかの答えは、これ? → 人生、宇宙、すべての答え - Google 検索

to_sentence

おもしろいけど使い道ありそうでなさそうな…。 配列を文章っぽくつなげてくれます。オプションやlocaleの設定でつなげる文字を返ることもできる。

%w(one two).to_sentence          # => "one and two"
%w(one two three).to_sentence # => "one, two, and three"
%w(one two three four).to_sentence(words_connector: ' & ', last_word_connector: ' &... ')
 # => "one & two & three &... four"

append と prepend

<<unshift をちょっと人に優しくしたエイリアス

[1, 2, 3].append(4) # => [1, 2, 3, 4]
[1, 2, 3].prepend(4) # => [4, 1, 2, 3]

グルーピング系

配列をいくつかの小さい配列に分割したり、複数の要素を同時に繰り返し処理するメソッドたち。 用途としては、例えばviewで3個ずつ横並びにするときなどに使えそう。

in_groups_of

指定した数が要素数になるような配列に分割。 あまったときに埋める値を第2引数で指定できる。デフォルトはnil

%w(1 2 3 4 5).in_groups_of(3) {|group| p group}
["1", "2", "3"]
["4", "5", nil]
in_groups

素数が均等な配列が、指定した数だけできるように分割。 最大長になるように埋める値を第2引数で指定できる。デフォルトはnil

%w(1 2 3 4 5).in_groups(3) {|group| p group}
["1", "2"]
["3", "4"]
["5", nil]
split

指定された要素を境にして複数の配列に分割。 文字列のsplitと同じイメージ。

(1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]]

Hash

参考: rails/activesupport/lib/active_support/core_ext/hash at master · rails/rails

compact と compact!

ArrayのcompactをHashにも。valueがnilのものを除きます。

deep_merge と deep_merge!

多次元ハッシュにも使えるmerge

h1 = { x: { y: [4, 5, 6] }, z: [7, 8, 9] }
h2 = { x: { y: [7, 8, 9] }, z: 'xyz' }

h1.deep_merge(h2) # => {x: {y: [7, 8, 9]}, z: "xyz"}
h2.deep_merge(h1) # => {x: {y: [4, 5, 6]}, z: [7, 8, 9]}

except と except!

keyを指定してそれを除外したHashを返す。

{ x: 'x', y: 'y', z: 'z' }.except(:y) # => {:x=>"x", :z=>"z"}
{ x: 'x', y: 'y', z: 'z' }.except(:y, :z) # => {:x=>"x"}

with_indifferent_access

なにげにありがたいメソッドActiveSupport::HashWithIndifferentAccessを生成して返します。 これは、keyに文字列・シンボルのどちらでも同じようにアクセスできるHashです。 Rails使ってるとControllerのparamとかでこいつの存在を感じます。 自分で作ったHashオブジェクトにRails風味の統一感を出したいときに使いましょう。

h1 = { a: 'aa' }
h1[:a] # => "aa"
h1['a'] # => nil

h2 = h1.with_indifferent_access
h2[:a] # => "aa"
h2['a'] # => "aa"

key変換系

transform_keys

keyを一括変換する。

hash = { name: 'Rob', age: '28' }
hash.transform_keys{ |key| key.to_s.upcase } # => {"NAME"=>"Rob", "AGE"=>"28"}
stringify_keys と symbolize_keys

keyを一括で文字列化 or シンボル化。

hash = { name: 'Rob', age: '28' }
hash.stringify_keys! # => {"name"=>"Rob", "age"=>"28"}
hash.symbolize_keys! # => {:name=>"Rob", :age=>"28"}

transform_keysstringify_keyssymbolize_keysにはそれぞれdeep_付きで多次元Hashにも対応できます。

assert_valid_keys

すべてのkeyが有効なものかチェックする。 有効でないkeyが含まれていたらArgumentErrorの例外。

{ name: 'Rob', years: '28' }.assert_valid_keys(:name, :age)
 # => raises "ArgumentError: Unknown key: :years. Valid keys are: :name, :age"
{ name: 'Rob', age: '28' }.assert_valid_keys(:name, :age)
 # => passes, raises nothing

slice と slice!

指定したkey以外のkeyを除外したHashを生成する。 これも何かと便利なメソッドですが、sliceslice! で返り値が真逆になるので少しだけ注意が必要。

hash = { a: 1, b: 2, c: 3 }
hash.slice(:a) # => {:a=>1}  返り値は指定したkeyのみを含むHash
hash # => {:a=>1, :b=>2, :c=>3} 非破壊なので元の変数は元のまま。


hash.slice!(:a) # => {:b=>2, :c=>3}  返り値は指定したkey以外のみを含むHash(除外される方)
hash # => {:a=>1} 破壊的なので元の変数が指定したkeyのみを含むHashに。

ちなみに、extract!slice!と逆のことができますが、extractはありません。なんでだろ…?

感想

日頃意識していなかったRails様の優しい心遣いに触れて胸が熱くなっています。

けどまとめるの意外と面倒だったのでもう他はやらないだろうなあ。 dateとかstringとかちょっと整理しときたい気もするなあ。 死ぬほど暇だったらやってもいいかなあ。