If str is your JSON string:
require 'json' arr = JSON.parse(str)["groups"]
The Enumerable # each_with_object method takes an argument, which is the initial value of the object that will be created and returned by the method. This value is set by the second block variable, h . I made this argument an empty hash with the default value given by the block:
{|h,k| h[k] = []}
What is the default value? All this means that if the hash h does not have the key k , h[k] returns an empty array. See how it works here.
Initially, h #=> {} and each_with_object sets the first block variable g be equal to the first value of arr :
g = {"values"=>"21", "date"=>"2013-02-22"}
and the block is calculated:
h[g["date"]] << g["values"].to_i #=> h["2013-02-22"] << 21
Since h does not have the key "2013-02-22" , h["2013-02-22"] first set to the default value, an empty array:
h["2013-02-22"] = []
then
h["2013-02-22"] << 21 #=> [21] h #=> {"2013-02-22"=>[21]}
When the following arr value is passed to the block:
g = {"values"=>"25", "date"=>"2013-02-22"}
and h has the meaning indicated above. So now the block calculation:
h[g["date"]] << g["values"].to_i #=> h["2013-02-22"] << 25 #=> [21, 25] h #=> {"2013-02-22"=>[21, 25]}
The default value is not used this time, since h has the key "2013-02-22" .
One more thing may require an explanation: "splat" * in:
min_vals, max_vals = *by_date.map { |_,vals| vals.minmax }
We see that:
by_date.map { |date, vals| vals.minmax }
If *by_date.map { |date, vals| vals.minmax } *by_date.map { |date, vals| vals.minmax } is on the right side of the equality, splat forces two elements [[19, 25], [11, 42]] to be assigned to the variables on the left side of the equality, using a parallel assignment. A strange and wonderful splat operator should be in every Rubiest trick bag.
Since I do not use date in the calculation of the block, I paid attention to this by replacing date with the local variable _ .
Edit: To answer the question you sent in the comment if:
id = [1,1,1,2,2,3,4] high = [100,100,100,90,90,100,100] low = [20,20,20,10,10,30,40]
and I understand your question correctly, you can first calculate:
indices = id.each_with_index.to_a.uniq(&:first).map(&:last)
Then three arrays are required:
id.values_at(*indices) #=> [1, 2, 3, 4] high.values_at(*indices) #=> [100, 90, 100, 100] low.values_at(*indices) #=> [20, 10, 30, 40]