Rails の便利なメソッド(Array 編)


この前、数値オブジェクトの拡張メソッドをまとめたので、次は Array の Rails 便利メソッドをまとめました。

可読性を向上させる

from(position), to(position)

位置を指定して値を取り出せる。from は引数の位置「から」、to は引数の位置「まで」。

[0, 1, 2, 3].from(1) #=> [1, 2, 3]
[0, 1, 2, 3].to(2) #=> [0, 1, 2]

from と同じことをしようとすると

[0, 1, 2, 3][1..-1] #=> [1, 2, 3]
[0, 1, 2, 3][1, 3] #=> [1, 2, 3]

という感じになるので、とても直観的。

second, third, fourth, fifth, forty_two

パッと見の通り、その位置の値を返す。Ruby に first があるので、その続きですね。

[1, 2, 3].second #=> 2
[1, 2, 3].third #=> 3
(1..42).to_a.forty_two #=> 42

append, prepend

append<< のエイリアス、prependunshift のエイリアス。
ただのエイリアスだけど、これも直観的なので、個人的にはよく使います。

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

グループ分けする

in_groups_of(number, fill_with = nil)

与えた number の長さの配列にグループ化する。
「10 人を 3 人ずつの班に分ける」というような使い方ができる。

(1..10).to_a.in_groups_of(3) #=> [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, nil, nil]]

デフォルトでは、要素数が number に足りない場合は nil が追加されるが、引数で指定する事も出来る。

(1..10).to_a.in_groups_of(3, 'hoge') #=> [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, "hoge", "hoge"]]

# false を渡すと補完しなくなる
(1..10).to_a.in_groups_of(3, false) #=> [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

さらにブロックで処理も与えられる。

('A'..'J').to_a.in_groups_of(3, false) {|g| puts "#{g.first}班 : #{g}" }
# A班 : ["A", "B", "C"]
# D班 : ["D", "E", "F"]
# G班 : ["G", "H", "I"]
# J班 : ["J"]

in_groups(number, fill_with = nil)

与えた number の数にグループ化する。
「10 人を 3 つの班に分ける」というような使い方ができる。

(1..10).to_a.in_groups(3) #=> [[1, 2, 3, 4], [5, 6, 7, nil], [8, 9, 10, nil]]
(1..10).to_a.in_groups(2) #=> [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]

この時、足りない要素数は 1 以内になるように分けてくれる。
また、同じように補完する値を指定可能。

(1..10).to_a.in_groups(3, 'hoge') #=> [[1, 2, 3, 4], [5, 6, 7, "hoge"], [8, 9, 10, "hoge"]]
(1..10).to_a.in_groups(3, false) #=> [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]

ブロックも

('A'..'J').to_a.in_groups(3, false) {|e| puts "#{e.first}班 : #{e}" }
# A班 : ["A", "B", "C", "D"]
# E班 : ["E", "F", "G"]
# H班 : ["H", "I", "J"]

split(value = nil)

指定した値にマッチ or ブロックの結果が true になるところで配列を分割する。

(1..5).to_a.split(3) #=> [[1, 2], [4, 5]]
[1, 2, 'a', 4, 5].split {|a| a.kind_of?(String) } #=> [[1, 2], [4, 5]]

文字列に変換する

to_sentence(options = {})

要素を連結して、文字列に変換する。
その際、以下のオプションが指定できる

オプション 説明
words_connector 要素を連結する際の文字列
two_words_connector 配列の要素が 2 の場合の連結文字列
last_word_connector 最後の要素を連結する際の文字列
locale I18n 対応、言語を指定出来る
['A', 'B', 'C'].to_sentence #=> "A, B, and C"

# 言語の指定
['A', 'B', 'C'].to_sentence(locale: :ja) #=> "AとBとC"

# 連結する文字列の指定
['A', 'B', 'C'].to_sentence(words_connector: ' と ') #=> "A と B, and C"
['A', 'B', 'C'].to_sentence(words_connector: ' と ', last_word_connector: ' そして ') #=> "A と B そして C"

# 要素が 2 つの場合は two_words_connector を使う
['A', 'B'].to_sentence(words_connector: ' と ', last_word_connector: ' そして ') #=> "A and B"
['A', 'B'].to_sentence(two_words_connector: ' あと ') #=> "A あと B"

全部のオプションを指定してみる

locales/ja.yml
ja:
  support:
    array:
      words_connector: '  '
      two_words_connector: ' そして '
      last_word_connector: ' 最後に '
('A'..'J').to_a.in_groups(4, false) {|g| puts "#{g.first}班 : #{g.to_sentence(locale: :ja)}" }
# A班 : A と B 最後に C
# D班 : D と E 最後に F
# G班 : G そして H
# I班 : I そして J

無理矢理(・・;

to_s(format = :default)

Numeric クラスで拡張されている事を紹介しましたが、Array クラスでも拡張されています。
ただ、format が :db しかないのと、id 属性があるオブジェクトの配列にしか使えないのが残念。

User.all.to_a.to_s(:db) #=> "1,2,3,4,5,6,7,8"

このように id を , 区切りで連結して返します。

to_param, to_query, to_xml(options = {})

左から URL のパス、クエリ、XML に変換した文字列を返す。

紹介しておいて何ですが、あんまり使い方が思いつかなかった。。。
まあ、ActionPack の Renderers とか Redirect で使われるているので、どちらかというと Rails 内で使うために用意されているのかな。ただ、ちょっとしたスクリプト書くときに便利かも。

%w(qiita.com pekepek items).to_param #=> "qiita.com/pekepek/items"
%w(Rails coding).to_query('hobbies') #=> "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"

puts [{foo: 1, bar: 2}, {baz: 'hogehoge'}].to_xml
# <?xml version="1.0" encoding="UTF-8"?>
# <objects type="array">
#   <object>
#     <foo type="integer">1</foo>
#     <bar type="integer">2</bar>
#   </object>
#   <object>
#     <baz>hogehoge</baz>
#   </object>
# </objects>

その他

self.wrap(object)

Kernel#Array 基本動作は同じだが、Range や Hash を引数にした場合に挙動が異なる。

# 引数が Array の場合
Array([1, 2, 3]) #=> [1, 2, 3]
Array.wrap([1, 2, 3]) #=> [1, 2, 3]

# Hash の場合
Array(a: 1, b: 2, c: 3) #=> [[:a, 1], [:b, 2], [:c, 3]]
Array.wrap(a: 1, b: 2, c: 3) #=> [{:a=>1, :b=>2, :c=>3}]

# Range の場合
Array(1..3) #=> [1, 2, 3]
Array.wrap(1..3) #=> [1..3]

ちゃんと配列にラップしてくれるメソッドという感じですね。

Rails 5 で追加されるメソッド

最後に Rails 5 で追加されるメソッドも紹介。

second_to_last, third_to_last

last の続きが追加された...!!

[1, 2, 3].third_to_last #=> 1
[1, 2, 3].second_to_last #=> 2

without(*elements)

elements で指定した要素を除いた配列を返す。中身は Array#- してるだけ。

[1, 2, 3].without(1, 2) #=> [3]

inquiry

元々 String#inquiry はあったが、Array にも追加された。
hogehoge? というメソッドが呼べるようになり、実際にメソッド名と同じ要素が合ったら true を返す。

users = %w(pekepek pokopok).inquiry #=> ["pekepek", "pokopok"]
users.pekepek? #=> true
users.pokopok? #=> true
users.tom? #=> false

include?('pekepek') とやっている事は同じですが、引数がないと見た目がすっきりしていいですね。

また、inquiry を呼ぶと、any? がオーバーライドされて、可変長引数を受け取れる様になります。元の any? と同じように引数が一つでもマッチしたら true を返します。

users = %w(pekepek pokopok).inquiry
users.any?('pekepek', 'hogehoge') #=> true
users.any?('hogehoge', 'fugafuga') #=> false

これは結構使いどころ多そうですね。

以上が Array の拡張メソッドです!