sschmeck
sschmeck

Reputation: 7685

map_values() for Ruby hashes?

I miss a Hash method in Ruby to transform/map only the values of the hash.

h = { 1 => [9,2,3,4], 2 => [6], 3 => [5,7,1] }
h.map_values { |v| v.size }
#=> { 1 => 4, 2 => 1, 3 => 3 } 

How do you archive this in Ruby?

Update: I'm looking for an implementation of map_values().

# more examples
h.map_values { |v| v.reduce(0, :+) }
#=> { 1 => 18, 2 => 6, 3 => 13 } 

h.map_values(&:min)
#=> { 1 => 2, 2 => 6, 3 => 1 }

Upvotes: 8

Views: 4375

Answers (9)

James Klein
James Klein

Reputation: 612

You can use hash.each_value.map { |value| puts(value) } to iterate over all values of a hash.

Upvotes: 0

sschmeck
sschmeck

Reputation: 7685

Ruby 2.4 introduced the methods Hash#transform_values and Hash#transform_values! with the desired behavoir.

h = { 1=>[9, 2, 3, 4], 2=>[6], 3=>[5, 7, 1] }
h.transform_values { |e| e.size }
#=> {1=>4, 2=>1, 3=>3}

Upvotes: 16

Drenmi
Drenmi

Reputation: 8777

You can also use Hash#update for this:

h = { 1 => [9, 2, 3, 4], 2 => [6], 3 => [5, 7, 1] }

h.update(h) { |_, v| v.size }
#=> { 1 => 4, 2 => 1, 3 => 3 }

It replaces all values that have duplicate keys in one hash with that of another, or, if a block is given, with the result of calling the block. You can pass the original hash as the argument to ensure all values are replaced.


Note that this modifies the hash in place! If you want to preserve the original hash, dup it first:

h.dup.update(h) { |_, v| v.size }
#=> { 1 => 4, 2 => 1, 3 => 3 }

h
#=> { 1 => [9, 2, 3, 4], 2 => [6], 3 => [5, 7, 1] }

Upvotes: 3

user12341234
user12341234

Reputation: 7203

There's an implementation of this method in the DeepEnumerable library: https://github.com/dgopstein/deep_enumerable/

It's called shallow_map_values:

>> require 'deep_enumerable'    
>> h = { 1 => [9,2,3,4], 2 => [6], 3 => [5,7,1] }    
>> h.shallow_map_values { |v| v.size }
=> {1=>4, 2=>1, 3=>3}

Upvotes: 1

steenslag
steenslag

Reputation: 80065

No map, just each

h = { 1 => [1,1,1,1], 2 => [2], 3 => [3,3,3] }
h.each{|k,v| h[k] = v.size}

Upvotes: 2

Wand Maker
Wand Maker

Reputation: 18762

Here is one more way to achieve it:

h = { 1 => [1,1,1,1], 2 => [2], 3 => [3,3,3] }
p h.keys.zip(h.values.map(&:size)).to_h
#=> {1=>4, 2=>1, 3=>3}

Upvotes: 1

Damiano Stoffie
Damiano Stoffie

Reputation: 897

You can monkey-patch the hash class, like this

class Hash
  def map_values
    map { |k, v|
      [k, yield(v)]
    }.to_h
  end
end

p ({1 => [1,1,1,1], 2 => [2], 3 => [3,3,3]}.map_values { |e| e.size })

Upvotes: 5

shivam
shivam

Reputation: 16506

You can achieve this by:

h.map { |a, b| [a, b.size] }.to_h
#=> {1=>4, 2=>1, 3=>3}

Upvotes: 1

punitcse
punitcse

Reputation: 727

This will do the trick for you

h = { 1 => [1,1,1,1], 2 => [2], 3 => [3,3,3] }
h.map {|k,v| [k, v.size] }.to_h

Upvotes: 2

Related Questions