Help us understand the problem. What is going on with this article?

Ruby / Railsで、key-value なオブジェクトから値を取得するイディオム

More than 1 year has passed since last update.

Ruby / Railsで、key-value なオブジェクトから値を取得するイディオム

by 3nan3
1 / 18

環境

Ruby 2.5.1
Ruby on Rails 5.0.5


きっかけ

Rails を触っていると、Hash から特定の key の値だけ取得したい時がある


hash = { key1: "value1", key2: "value2", key3: "value3" }

hash.slice(:key1, :key2)
=> {:key1=>"value1", :key2=>"value2"}

違う、そうじゃない


おれは値だけがほしいんだ

hash.slice(:key1, :key2)
=> {:key1=>"value1", :key2=>"value2"}
# ↓ 本当はこうしたい
=> ["value1", "value2"]

> hash.extract(:key1, :key2)
NoMethodError: undefined method `extract` for {:key1=>"value1", :key2=>"value2", :key3=>"value3"}:Hash
...

> hash.pluck(:key1, :key2)
TypeError: no implicit conversion of Symbol into Integer
...

> hash.attributes(:key1, :key2)
NoMethodError: undefined method `attributes` for {:key1=>"value1", :key2=>"value2", :key3=>"value3"}:Hash
...


んーなんだっけ?

といつも自分でもわからなくなるので、似たような目的のメソッドとか書き方を自分用としてまとめました


ちなみに、さきほどの問題に対しての回答は

hash.values_at(:key1, :key2)
=> ["value1", "value2"]

でした


Hash

hash = { key1: "value1", key2: "value2", key3: "value3" }

# 値だけ
hash.values_at(:key1, :key2)
=> ["value1", "value2"]

# Hash として
hash.slice(:key1, :key2) # ruby 2.5.0 で ActiveSuport から輸入
=> {:key1=>"value1", :key2=>"value2"}

Hash[]

> hashes = [{ key1: "value1-1", key2: "value2-1"}, { key1: "value1-2", key2: "value2-2"}]

# 値だけ
hash.pluck(:key1) # rails 5.0 からの Enumerable 拡張
=> ["value1-1", "value1-2"]

# Hash として
hashes.map { |h| h.slice(:key1) }
=> [{:key1=>"value1-1"}, {:key1=>"value1-2"}]

ApplicationRecord

message = Message.take

# 値だけ
user.attributes.values_at("title", "text") # #attributes は Hash を返すので、キーの指定が文字列である必要がある
or
user.slice(:title, :text).values # Ruby 1.9 から Hash は順序を保存するようになったのでこれでも大丈夫
=> ["タイトル1", "内容1"]

# Hash として
user.slice(:title, :text)
=> {"title"=>"タイトル1", "text"=>"内容1"} # with_indifferent_access されて帰ってくるので、文字列でもシンボルでもアクセス可能

ActiveRecord::Associations

users = User.take(2)

# 値だけ
users.pluck(:title, :text)
=> [["タイトル1", "内容1"], ["タイトル2", "内容2"]]

# Hash として
users.map { |u| u.slice(:title, :text) }
=> [{"title"=>"タイトル1", "text"=>"内容1"}, {"title"=>"タイトル2", "text"=>"内容2"}]

おまけ

似たようなメソッドがいっぱいあるぞ!


Hash#extract!

最初に間違えたやつのBANメソッド
何故か #extract は存在しない
動作は #slice! と同様

hash = { key1: "value1", key2: "value2", key3: "value3" }

hash.extract!(:key1, :key2)
=> {:key1=>"value1", :key2=>"value2"}
hash
=> {:key3=>"value3"}

Hash#except

#slice の逆
特定のキーとその値を除外したい時に使える

hash = { key1: "value1", key2: "value2", key3: "value3" }

hash.except(:key3)
=> {:key1=>"value1", :key2=>"value2"}

Hash#fetch_values

#values_at と似たような挙動をするメソッド
キーが見つからなければ KeyError が発生する(#values_atnilが返る)
ブロックを渡すと KeyError の代わりに、その評価値が返る

hash = { key1: "value1", key2: "value2", key3: "value3" }

hash.fetch_values(:key1, :key2)
=> ["value1", "value2"]
hash.fetch_values(:key1, :key3)
KeyError: key not found: :key3
hash.fetch_values(:key1, :key3) { :not_found }
=> ["value1", :not_found]

Hash#assoc

Hash のキーを指定すると、引っかかった key-value の配列を返す
キーの指定は1つのみ

元々は、多次元配列に対して要素を取り出すのが目的な感じに見える(Arrayでも使えるし)

{ key1: "value1", key2: "value2" }.assoc(:key1)
=> [:key1, "value1"]
[[:key1, "value1"], [:key2, "value2"]].assoc(:key1)
=> [:key1, "value1"]

ちなみに、rassocというメソッドもあり、こちらはキーではなく値を引数に指定する

3nan3
aktsk
株式会社アカツキは、スマートフォンゲームの企画開発を中心に事業を展開しております。創業以来全てのゲームを内製しているため、高い技術ノウハウが蓄積されています。今後は、新規事業の立ち上げも行ってまいります。
http://aktsk.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした