Chick Evans
Chick Evans

Reputation: 47

Update a Hash Where the Values are Hashes

I have this hash where the keys are 0, 3, and 5 and the values are hashes.

{0=>{:occurrences=>1, :className=>"class"},
 3=>{:occurrences=>3, :className=>"hello"},
 5=>{:occurrences=>3, :className=>"nah"}}

How can I implement something like this:

h.map { |key| 
  if key[:occurrences] > 2
    key[:occurrences] += 1 
  end
}

I know this syntax doesn't work. I want to increment the occurrence value when a condition is met and I am not sure how to access the key of a key but I would like the result to be:

{0=>{:occurrences=>1, :className=>"class"}, 
 3=>{:occurrences=>4, :className=>"hello"}, 
 5=>{:occurrences=>4, :className=>"nah"}}

Upvotes: 0

Views: 68

Answers (4)

Cary Swoveland
Cary Swoveland

Reputation: 110675

h = { 0=>{ :occurrences=>1, :className=>"class" },
      3=>{ :occurrences=>3, :className=>"hello" },
      5=>{ :occurrences=>3, :className=>"nah"   } }

f = h.dup

Non-destructive case

h.transform_values do |g|
  g[:occurrences] > 2 ? g.merge(occurrences: g[:occurrences] + 1) : g
end
  #=> { 0=>{ :occurrences=>1, :className=>"class" }, 
  #     3=>{ :occurrences=>4, :className=>"hello" },
  #     5=>{ :occurrences=>4, :className=>"nah"   } }

h == f
  #=> true

Destructive case

g = h.transform_values! do |g|
  g[:occurrences] > 2 ? g.merge(occurrences: g[:occurrences] + 1) : g
end
  #=> { 0=>{ :occurrences=>1, :className=>"class" },
  #     3=>{ :occurrences=>4, :className=>"hello" },
  #     5=>{ :occurrences=>4, :className=>"nah"   } }
h == g

See Hash#transform_values and Hash#transform_values!, which made their debut in MRI v.2.4. Note that, in the destructive case, merge! is not needed.

Upvotes: 1

Stefan
Stefan

Reputation: 114158

To update the existing hash you can simply call each_value. It passes each value in your hash to the block and within the block you can update the value (based on a condition):

h = {
  0=>{:occurrences=>1, :className=>"class"},
  3=>{:occurrences=>3, :className=>"hello"},
  5=>{:occurrences=>3, :className=>"nah"}
}

h.each_value { |v| v[:occurrences] += 1 if v[:occurrences] > 2 }
#=> {
#     0=>{:occurrences=>1, :className=>"class"},
#     3=>{:occurrences=>4, :className=>"hello"},
#     5=>{:occurrences=>4, :className=>"nah"}
#   }

Upvotes: 1

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

Just out of curiosity:

input.map do |k, v|
  [k, v[:occurrences].to_i > 2 ? v.merge(occurrences: v[:occurrences] + 1) : v]
end.to_h
#⇒ {0=>{:occurrence=>1, :className=>"class"},
#   3=>{:occurrences=>4, :className=>"hello"},
#   5=>{:occurrences=>4, :className=>"nah"}}

Upvotes: 1

Martin
Martin

Reputation: 4222

For example I would want the entire hash returned but with updated values: {0=>{:occurrence=>1, :className=>"class"}, 3=>{:occurrences=>4, :className=>"hello"}, 5=>{:occurrences=>4, :className=>"nah"}}. If the value of the occurrences key is greater than two than I want to increment that value and still have the entire hash.

Here you go:

h = {0=>{:occurrences=>1, :className=>"class"}, 3=>{:occurrences=>3, :className=>"hello"}, 5=>{:occurrences=>3, :className=>"nah"}}

new_h = h.map do |k, v| 
  if v[:occurrences] > 2
    v[:occurrences] += 1 
  end

  [k, v]
end.to_h

Upvotes: 0

Related Questions