Transpose a hash in a ruby ​​with zip

Is there a simpler permutation of the next hash? I have a solution that works, but the transpose method is hard to read. A use case is a hash of tasks (j1, j2, j3, etc.) and the dates on which they occur (d1, d2, d3, etc.). The requirement is to accept a hash of events (e1, e2, e3, etc.) that are grouped by date, then by task, and transpose them to events grouped by task, and then by date .

I experimented with the #zip method, but for this you will need to enter nil in j1: d1, and then remove them from the results. I also tried to find an example of using #zip with a block argument. I understand that #zip with a block always returns zero , but as a result, I could not figure out how you ever used it.

require 'rspec' require 'pry' # | d1 | d2 | d3 | # ---------------------- # j1 | | e2 | e3 | # -------------------------- # j2 | e4 | e5 | e6 | # -------------------------- # j3 | e7 | | e9 | # -------------------------- def transpose(h) Hash[ dates(h).map do |d| [ d, Hash[ h.keys.map do |j| h[j][d] ? [j, h[j][d]] : nil end.compact ] ] end ] end def dates(h) h.values.map(&:keys).reduce(:|).sort end describe "transpose" do let(:starting) { { j1: { d2: :e2, d3: :e3 }, j2: { d1: :e4, d2: :e5, d3: :e6 }, j3: { d1: :e7, d3: :e9 } } } let(:transposed) { { d1: { j2: :e4, j3: :e7 }, d2: { j1: :e2, j2: :e5 }, d3: { j1: :e3, j2: :e6, j3: :e9 } } } it { expect(dates(starting)).to eq([:d1, :d2, :d3]) } it { expect(transpose(starting)).to eq(transposed) } end 
+5
source share
1 answer

I rewrote your transpose method a bit, it should be faster and cleaner:

  def transpose(h) h.each_with_object({}) do |(outer, data), ret| data.each do |inner, event| ret[inner] = {} unless ret[inner] ret[inner][outer] = event end end end 

It does not use unnecessary date display and works in both directions (changes internal and foreign keys). Let me know what you think :)

+2
source

All Articles