Must-know hash initialization tricks in Ruby

  • Post last modified:March 19, 2023
  • Reading time:3 mins read

discussing various options to initialize hash in ruby

Introduction

  • Let’s consider we have an array of the array as input. array elements basically represent order_id and product_id with that. So basically for order_id 1, product_id 2 was bought.
[[1,2],[1,3],[2,3],[2,2],[3,1]]
  • Now let’s say we want to calculate for each product_id which products were bought.
  • Now this problem basically tells us to output something like Key Value, where the key is order_id and the value is a list of products.
  • So for order_id 1 output would be -> { 1 => [2,3] }

Solution using external Hash

  • One of the ways to write a solution is to create a Hash and initialize it with an array using a block
  • Now when we iterate over our element using each we can add each product to its corresponding order_id which is key in our case and val is product_id.
  • Hence for each key, we add value to our array using shovel operator << which is essentially appending or pushing an element to our array.
 h = Hash.new { |h, k| h[k] = [] }

[[1,2],[1,3],[2,3],[2,2],[3,1]].each do |(key, val)|
    h[key] << val
end
irb(main):021:0> h
=> {1=>[2, 3], 2=>[3, 2], 3=>[1]}    #ouput

Improving solution by injecting Hash

  • We can make our code more concise by using reduce operator instead of each.
  • reduce operator takes an accumulator which we can initialize in reduce.
  • Now, this is a good place to initialize our hash since we will need to collect all the product_id for a given key.
  • Once we add the product_id, we return a hash so that at the end of the array we will be able to get our output.
[[1,2],[1,3],[2,3],[2,2],[3,1]].reduce(Hash.new { |h,k|  h[k] = [] } ) do | hash, (key,val) |
      hash[key] << val
      hash
 end

=> {1=>[2, 3], 2=>[3, 2], 3=>[1]} # ouptut

Some more examples

  • Now we can use the above improvement in other use cases.
  • For example, let’s say now the input array has product_id and a number of sales made at different points in time.
  • Now we want to calculate how many of each product sold.
  • Now our solution would require us to add the value of each key.
  • We can again inject a hash map and initialize all the keys with the value 0
[[1,2],[1,3],[2,3],[2,2],[3,1]].reduce(Hash.new { |h,k|  h[k] = 0 } ) do | hash, (key,val) |
   hash[key] +=val
   hash
 end

=> {1=>5, 2=>5, 3=>1} # output
  • We can also initialize hash with 0 simply by injecting Hash.new(0)
[[1,2],[1,3],[2,3],[2,2],[3,1]].reduce(Hash.new(0) ) do | hash, (key,val) |
   hash[key] +=val
   hash
end

=> {1=>5, 2=>5, 3=>1} # output
  • One of the alternative ways of achieving the same thing is using each_with_object method, which also allows us to inject hash with initialized value.
[[1,2],[1,3],[2,3],[2,2],[3,1]].each_with_object(Hash.new(0)) do |(key,val), h|
      h[key] += val
 end
=> {1=>5, 2=>5, 3=>1} # output

Conclusion

  • In this article we looked at different options we have in ruby to initialize with the default value.
  • reduce and each_with_object are good target methods to inject initialize objects when needed.
  • Let me know in the comments if you know an additional approach.

Leave a Reply