LoginSignup
1
1

More than 5 years have passed since last update.

Ruby で map するときに気をつけていること

Posted at

Ruby で map を用いて配列から新しい配列を作る場合、以下のように配列の中身に気をつける必要があります。

sample1.rb
original = [
  {key: "foo"},
  {key: "bar"}
]
mapped = original.map do |obj|
  obj[:hoge] = "fuga"
  obj
end
original == mapped # => true
original # => [{:key=>"foo", :hoge=>"fuga"}, {:key=>"bar", :hoge=>"fuga"}]

配列内のハッシュに hoge: "fuga" を追加した新しい配列を map で生成するコードですが、original も書き換わってしまいます。

ハッシュの配列を map する場合

original を書き換えないために dup を使います。

sample2.rb
original = [
  {key: "foo"},
  {key: "bar"}
]
mapped = original.map do |original_obj|
  obj = original_obj.dup
  obj[:hoge] = "fuga"
  obj
end
original == mapped # => false
original # => [{:key=>"foo"}, {:key=>"bar"}]

何故 map しているのに original が書き換わるのか

original[0] には ハッシュオブジェクト {key: "foo"} が格納されているのではなく、ハッシュオブジェクト {key: "foo"}参照 が格納されています。

sample1.rb の場合 map のブロック変数 obj には、配列の各要素であるハッシュの 参照が渡される ため、obj に対し代入等の操作を行うと、original の各要素が参照しているハッシュオブジェクトが変更されます。

また、map の返り値は obj となっているため mapped の各要素は original の各要素と同じハッシュオブジェクトの参照を格納している 事になります。

Ruby は値渡しですが、変数が参照を保持している場合は 参照の値渡し となることに注意が必要です。

詳しくは、Rubyist Magazine - 値渡しと参照渡しの違いを理解するを参照してください。

ちなみに original.dup.map の場合

以下のように original が書き換わってしまいます。

sample3.rb
original = [
  {key: "foo"},
  {key: "bar"}
]
mapped = original.dup.map do |obj|
  obj[:hoge] = "fuga"
  obj
end
original == mapped # => true
original # => [{:key=>"foo", :hoge=>"fuga"}, {:key=>"bar", :hoge=>"fuga"}]

dup は浅い複製 (shallow copy)のため、original.dup を行っても、配列内の各要素がハッシュオブジェクトの参照のままであるため、original が書き換わってしまいます。

以下のように pry を使って object_id を確認すると、originalclonedobject_id は異なりますが、配列の第1要素は同じオブジェクトを指していることが分かります。

pry
[1] pry(main)> original = [{key: "foo"}, {key: "bar"}]
=> [{:key=>"foo"}, {:key=>"bar"}]
[2] pry(main)> cloned = original.dup
=> [{:key=>"foo"}, {:key=>"bar"}]
[3] pry(main)> original.object_id
=> 70201156677940
[4] pry(main)> cloned.object_id
=> 70201161775220
[5] pry(main)> original[0].object_id
=> 70201156678620
[6] pry(main)> cloned[0].object_id
=> 70201156678620
[7] pry(main)>
1
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1