Next key/value pair overwrites the existing pair i

2019-08-19 02:55发布

问题:

I have:

fruits = {
  "orange" => {:season => "winter"},
  "apple" => {:season => "winter"},
  "banana" => {:season => "summer"},
  "grape" => {:season => "spring"},
  "peach" => {:season => "winter"},
  "pineapple" => {:season => "summer"}
}

I want to get:

{
  "winter"=>["orange", "apple", "peach"],
  "summer"=>["banana", "pineapple"],
  "spring"=>["grape"]
}

I did:

def sort_fruits(fruits_hash)
  fruits=[]
  sorted = {}
  seasons = fruits_hash.map {|k, v|v[:season]}
  seasons.uniq.each do |season|
    fruits.clear
    fruits_hash.each do |fruit, season_name|
      if season == season_name[:season]
        fruits << fruit
      end
    end
    p sorted[season] = fruits ## season changes to new season, so this should have created new key/value pair for new season.
  end
  sorted
end

I get:

{
  "winter"=>["grape"],
  "summer"=>["grape"],
  "spring"=>["grape"]
}

I couldn't figure out why adding new key/value pair with unique key would overwrite existing pair in a hash. Any help with explanation would be greatly appreciated.

回答1:

In Ruby mutable objects are passed by reference. It means that when you iterate over seasons in each block this line:

sorted[season] = fruits

saves to sorted[season] a reference to fruits, for every season. After each loop finishes every season has a reference to the same fruits array, which contain items calculated on the last step of the iterator. In your case, it's ["grape"].



回答2:

Your problem is that you reuse the same fruits array for all the values. Even though you clear it, is is still the same array. If instead of fruits.clear you use fruits = [] then you won't have the issue.

You can see the issue in the following as an example:

arr = ['val']
hash = {
  key1: arr,
  key2: arr
}
p hash # => { key1: ['val'], key2: ['val'] }

arr.clear
p hash # => { key1: [], key2: [] }

You could alternatively use sorted[season] = fruits.clone or sorted[season] = [*fruits] ... anything that uses a new array.

You have to keep track of when you use 'mutation' methods (those that change objects in-place such as clear) - this is a common pitfall when working with hashes and arrays