Search code examples
rubyarrayshash

How do I map an array of hashes?


I have an array of hashes:

arr = [ {:a => 1, :b => 2}, {:a => 3, :b => 4} ]

What I want to achieve is:

arr.map{|x| x[:a]}.reduce(:+)

but I think it's a bit ugly, or at least not that elegant as:

arr.map(&:a).reduce(:+)

The later one is wrong because there is no method called a in the hashes.

Are there any better ways to write map{|x| x[:a]}?


Solution

  • You could make actual Objects, possibly with a Struct:

    MyClass = Struct.new :a, :b
    arr = [MyClass.new(1, 2), MyClass.new(3, 4)]
    arr.map(&:a).reduce(:+)  #=> 4
    

    Or for more flexibility, an OpenStruct:

    require 'ostruct'
    arr = [OpenStruct.new(a: 1, b: 2), OpenStruct.new(a: 3, b: 4)]
    arr.map(&:a).reduce(:+)  #=> 4
    

    Of course either of these can be constructed from existing hashes:

    arr = [{ :a => 1, :b => 2 }, { :a => 3, :b => 4 }]
    
    ss = arr.map { |h| h.values_at :a, :b }.map { |attrs| MyClass.new(*attrs) }
    ss.map(&:a).reduce(:+)  #=> 4
    
    oss = arr.map { |attrs| OpenStruct.new attrs }
    oss.map(&:a).reduce(:+)  #=> 4
    

    Or, for a more creative, functional approach:

    def hash_accessor attr; ->(hash) { hash[attr] }; end
    arr = [{ :a => 1, :b => 2 }, { :a => 3, :b => 4 }]
    arr.map(&hash_accessor(:a)).reduce(:+)  #=> 4