#ruby Array of Hashes Quiz
Found this interesting ruby quiz from AlphaSights.
Given an array of hashes, collapse into an array of hashes containing one entry per day.
And you can only reference the :time
key and not the rest.
log = [
{time: 201201, x: 2},
{time: 201201, y: 7},
{time: 201201, z: 2},
{time: 201202, a: 3},
{time: 201202, b: 4},
{time: 201202, c: 0}
]
# result should be
[
{time: 201201, x: 2, y: 7, z: 2},
{time: 201202, a: 3, b: 4, c: 0},
]
The first thing came to mind is to use Enumerable#group_by
grouped = log.group_by { |i| i[:time] }
collapsed = grouped.collect do |t, a|
no_time_h = a.inject({}) do |others, h|
others.merge h.reject { |k, v| k.to_sym == :time }
end
{time: t}.merge(no_time_h)
end
puts collapsed.inspect
However, after reading this a couple of times, I still find the solution hard to follow.
For starter, group_by
returns a hash where the values are an array of hashes which
brings me back to the original problem even though it is already grouped by time.
That I feel made the rest of the code more complicated.
# result of group_by
{201201=>[{:time=>201201, :x=>2}, {:time=>201201, :y=>7}, {:time=>201201, :z=>2}], 201202=>[{:time=>201202, :a=>3}, {:time=>201202, :b=>4}, {:time=>201202, :c=>0}]}
For my second version, I simply loop into the array and compose the hash using :time
as the key.
Afterwards, use the key-value
pair to compose the resulting array. The code may be longer but
it is more readable. Remember, Correct, Beautiful, Fast (in That Order).
hash_by_time = {}
log.each do |h|
time = h[:time]
others = h.reject { |k,v| k.to_sym == :time }
if hash_by_time[time]
hash_by_time[time].merge! others
else
hash_by_time[time] = others
end
end
collapsed = hash_by_time.collect do |k, v|
{time: k}.merge(v)
end