Reputation: 1078
My situation is quite sophisticated, that's why I replaced original data with simple numbers. So, please, don't pay attention to very simple data and 'idiotic' if conditions. It's just an example. Also, please, ignore typos if there's any - original code has no typos.
I have array of hashes with elements like my_hsh = {"numbers" => [1, 2, 3, 4], "n_count" => 4}
What I need to do:
my_arr_nochange
,the_hash["numbers"]
,my_arr_updt
.So the code:
the_hash = {"numbers" => []}
my_arr_updt = []
my_arr_nochange = []
array_of_hashes.each do |my_hsh|
if my_hsh["n_count"] == 4
my_arr_nochange << my_hsh
updated_hsh = my_hsh
my_hsh["numbers"].each do |num|
if num == 2
the_hash["numbers"] += [ num ]
updated_hsh["numbers"] -= [ num ]
end
end
my_arr_updt << updated_hsh
end
end
return the_hash, my_arr_updt, my_arr_nochange
The problem is my_arr_nochange
is being modified, so instead of getting the old state of my_hsh inside I get the new one. Like:
my_arr_updt
=> [{"numbers" => [1, 3, 4], "n_count" => 4}]
my_arr_nochange
=> [{"numbers" => [1, 3, 4], "n_count" => 4}]
Tried splitting in different methods and using sub-variables. No result.
P.S.: If you could help with more appropriate title, I would appreciate that too.
Upvotes: 1
Views: 69
Reputation: 26778
I believe your issue is updated_hsh = my_hsh
.
This does not duplicate the hash. Any changes to updated_hsh
will change my_hsh
and vice versa.
Using Object#clone or Object#dup is a step in the right direction, but won't actually duplicate inner objects (the "numbers" arrays):
h1 = [{numbers: [1,2,3], n_count: 3}]
h2 = h1.map(&:clone)
h2[0][:numbers] << 4
h2[0][:n_count] += 1
h1
# => [{numbers: [1,2,3,4], n_count: 3}]
You can see that n_count
was not altered in the original, but numbers
was.
To get around this, you can use Hash#deep_dup. This method isn't available in Ruby core. It is part of Active Support which is required by Rails, and can easily be loaded to a plain Ruby program as well, if you require the gem.
require 'active_support/all'
h1 = [{numbers: [1,2,3], n_count: 3}]
h2 = h1.map(&:deep_dup)
h2[0][:numbers] << 4
h2[0][:n_count] += 1
h1
# => [{numbers: [1,2,3], n_count: 3}]
You can also implement deep_dup
yourself, if you don't want to use active support. See How to create a deep copy of an object in Ruby?
Another alternative is to manually build the inner hashes, like so:
h1 = [{numbers: [1,2,3], n_count: 3}]
h2 = h1.map do |hsh|
{
numbers: hsh[:numbers].clone,
n_count: hsh[:n_count]
}
end
Although deep_dup
is more concise
Upvotes: 6