私は現在、未経験からのエンジニア転職に向けてプログラミングスクールで学習をしている、いしかわと申します。
mapメソッド
とindex_byメソッド
について理解を深めたく学習した内容をアウトプットとしてこちらの記事にしました。
どなたかの参考になれば幸いです。
プログラミング初学者なので、内容に誤り等ある可能性があります
誤りがありましたら教えてくださると幸いです
mapメソッド
配列の要素の数だけブロック内で処理を繰り返して、新しい配列を返す。
hoges(配列).map { |hoge| 実行する処理 }
# 実行する処理が複数行に渡る場合
hoges(配列).map do |hoge|
実行する処理
user_ids = all_requests.map { |request| request.user_id }
user_ids = [1, 2, 3, 4, 5, 6, ..... , 100]
# all_requestsに1~100のuser_idが含まれている場合, このように記述することでuser_idsにall_requestsに含まれるuser_idの配列が格納される
この記述方法は&:
を使うことでより簡潔に記述することができる
user_ids = all_requests.map(&:user_id)
ただし以下3つの条件をクリアした場合のみ&:
を使用することができる
・ブロックの引数が1つであること
・ブロックで呼び出すメソッドに引数がないこと
・ブロック引数に対して、メソッドを呼び出すこと以外の処理がないこと
これまで学習する中で&:
という記述はよく見かけましたが、実際どんなやつかさっぱり分からなかったので調べました
&と:
驚いたのが「&:」
この記法そのものには名前がないということ。&.(ぼっち演算子)にはあるのにね。
そもそも組み合わさったものではなく、それぞれ独立した&
と:
であり
&:
は
・&(アンパサンド)
-> ブロックをProcオブジェクトに変換する演算子Proc
オブジェクトをブロックとしてメソッドに渡す演算子
・:(シンボル)
-> Rubyのシンボル。シンボルは変更不可能な文字列のようなもので、メソッド名やキーに使用される
上記2つに分けることができ、、Procオブジェクトをブロックとして扱うことが可能になります&:
とすることで
3/6追記
@scivolaさんからご指摘いただいた内容について修正しました。
&:
は&
と:
が組み合わさっているわけではありませんでした。
それぞれが独立して働いています。
よって&:
に名前が名称がないのではなく、それぞれが独立したものであるため名称がないだけでした。
また&
は「ブロックをProcオブジェクトに変換する演算子」ではなく「Procオブジェクトをブロックとしてメソッドに渡す演算子」の誤りでした!
@scivolaさんありがとうございます!
Symbol#to_proc
リファレンスマニュアル読んだけど、いつもどおりなるほどわからんなので調べる
Procオブジェクト:
ブロックをオブジェクト化したProcクラスのインスタンス
Procクラス:
ブロックをオブジェクトとして扱うためのクラス
Procオブジェクト
はメソッドとして定義されていないコードブロックをcallメソッドで呼び出すことでメソッドのように扱うことができる
say_hello_proc = Proc.new { puts "Hello, world!" }
say_hello_proc.call # 出力: "Hello, world!"
class Greeting
def say_hello
puts "Hello, World!"
end
end
Procオブジェクトをcallメソッド
で呼び出すメリットは
・どこでも定義して使用でき、他のメソッドやProcに渡すことができる
・名前を定義する必要がなく、一時的な使用やコンパクトな記述に適している
デメリットは
・コードが分散するため可読性が低下する
・メソッド名として定義しないため、デバック時にバグの原因が見つけづらくなる
・メソッド呼び出しよりも僅かに呼び出しが遅いことがある
mapメソッドを使ってデータベースから特定の値だけ配列として取得する方法
users = User.all
users.map { |user| user.name }
#または
users.map(&:name)
# => ["Jacob", "Jackson", "Noah", "Lucas", "Sophia", "Chloe", "Abigail", "Harry"]
index_byメソッド
Railsドキュメントを読んだけどイマイチわからないので試してみる
※RequestオブジェクトにはcreateしたUserのidがuser_id
として一緒に保存されています
※RequestオブジェクトはGroupごとに生成されています
def index
all_requests = Request.includes(:user).where(group_id: params[:group_id]).order(updated_at: :desc)
user_ids = all_requests.map(&:user_id)
@users = User.where(id: user_ids).index_by(&:id)
binding.b
end
こんな感じでデバッグしてみると
(ruby) user_ids
[30, 29]
(ruby) User.where(id: user_ids)
CACHE User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" IN ($1, $2) [["id", 30], ["id", 29]]
↳ app/controllers/requests_controller.rb:20:in `completed'
[#<User id: 29, email: "hogehoge@example.com", created_at: "2023-12-02 04:51:56.263247000 +0900", updated_at: "2023-12-02 04:51:56.263247000 +0900", provider: nil, uid: nil>,
#<User id: 30, email: "fugafuga@example.com", created_at: "2023-12-02 04:53:53.895742000 +0900", updated_at: "2023-12-02 04:53:53.895742000 +0900", provider: nil, uid: nil>]
(ruby) @users
{29=>
#<User id: 29, email: "hogehoge@example.com", created_at: "2023-12-02 04:51:56.263247000 +0900", updated_at: "2023-12-02 04:51:56.263247000 +0900", provider: nil, uid: nil>,
30=>
#<User id: 30, email: "fugafuga@example.com", created_at: "2023-12-02 04:53:53.895742000 +0900", updated_at: "2023-12-02 04:53:53.895742000 +0900", provider: nil, uid: nil>}
user_ids = all_requests.map(&:user_id)
# => [30, 29]
この記述でall_requests
からuser_id
を抽出し、配列としてuser_ids
に格納
@users = User.where(id: user_ids).index_by(&:id)
# =>
{29=>
#<User id: 29, email: "hogehoge@example.com", created_at: "2023-12-02 04:51:56.263247000 +0900", updated_at: "2023-12-02 04:51:56.263247000 +0900", provider: nil, uid: nil>,
30=>
#<User id: 30, email: "fugafuga@example.com", created_at: "2023-12-02 04:53:53.895742000 +0900", updated_at: "2023-12-02 04:53:53.895742000 +0900", provider: nil, uid: nil>}
User.where(id: user_ids)
でuser_ids
に格納されたuser_id
の値とUserクラスを照会。この段階で返り値のUserオブジェクトは配列に格納されている
.index_by(&:id)
で配列で格納されているUserオブジェクトを{id(キー)=>value}
に変換している
index_by(&:id)
は配列をid
要素をキーとし、その他の情報をvalueとするハッシュに変換している
つまりindex_by(:&id)
メソッドはUser.where(id: user_ids)
によって格納された配列(ブロック, Procオブジェクト)に対して&:id
を使うことによってidメソッド
をブロックごとに走らせid
をキーとしたハッシュに変換している
参考記事
https://qiita.com/tatsu0209/items/ec96974b536f2d685928
https://qiita.com/s_tatsuki/items/869af9c0c33d9d650f3f
https://pikawaka.com/ruby/map
https://zenn.dev/keyproducts/articles/e8e3c8aca68f3d