オブジェクトの変数同士をマージしたいことがあったので、その時に書いたコードを共有します。
class A
attr_accessor [:foo, :bar, :hoge, :baz, :piyo]
def initialize(initial_props = {})
initial_props.each do |prop_name, prop_value|
self.send("#{prop_name}=", prop_value)
end
end
def to_h
hash = {}
instance_variables.each do |val|
if self.send(val.to_s.delete("@"))
hash[val.to_s.delete("@")] = self.send(val.to_s.delete("@"))
end
end
hash
end
def merge(another)
self.class.new(to_h.merge(another.to_h))
end
end
initialize
attr_accessor [:foo, :bar, :hoge, :baz, :piyo]
def initialize(initial_props = {})
initial_props.each do |prop_name, prop_value|
self.send("#{prop_name}=", prop_value)
end
end
hashでpropertyを初期化できるようにしておきます。
to_hを定義
def to_h
hash = {}
instance_variables.each do |val|
if self.send(val.to_s.delete("@"))
hash[val.to_s.delete("@")] = self.send(val.to_s.delete("@"))
end
end
hash
end
まずこのto_hメソッドを定義してインスタンス変数をhashとして返せるようにします。このときif文でnilのものはhashに含まないようにします。
mergeを定義
def merge(another)
self.class.new(to_h.merge(another.to_h))
end
続いて上で作成したto_hメソッドを使ってmergeを定義します。中でHash#mergeを使って変数同士をmergeしています。to_hメソッドでnilをhashに含めなかったのはこのHash#mergeを使うためです。そうでないと普通にanother_a_objで上書きする事になってしまいます。マージしたhashから新たにインスタンスを作成して返します
使ってみる
a_1 = A.new({foo: "foo", bar: "bar"})
=> #<A:0x007f953db0e620 @bar="bar", @foo="foo">
a_2 = A.new({hoge: "hoge", baz: "baz"})
=> #<A:0x007f953db24088 @baz="baz", @hoge="hoge">
a_1.merge(a_2)
=> #<A:0x007f953b662d30 @bar="bar", @baz="baz", @foo="foo", @hoge="hoge">
//値が被った場合は引数側で上書きされる
pry(main)> a_1 = A.new({foo: "foo", bar: "bar"})
=> #<A:0x007f953b623680 @bar="bar", @foo="foo">
pry(main)> a_2 = A.new({foo: "foo_modified", baz: "baz"})
=> #<A:0x007f953da9d0b0 @baz="baz", @foo="foom_odified">
pry(main)> a_1.merge(a_2)
=> #<A:0x007f953dad7d50 @bar="bar", @baz="baz", @foo="foo_modified">
うまくいきました!
もっと良いやり方があれば是非ご意見お待ちしていますm
(コメント欄にあるfrom_kyusyuさんにコメント頂きましたコードでエラー処理もされたより良いものも書いてありますのでそちらもご覧ください。)