nmm59
nmm59

Reputation: 15

Structuring ruby multidimensional hash by first hash

I have the hash below:

mm = {
    0 => {
        0 => 'p1',
        1 => 'p2',
        2 => 'p3'
    },
    1 => {
        0 => 'idfp1',
        1 => 'idfp2',
        2 => 'idfp3'
    },
    2 => {
        0 => 'idfp12',
        1 => 'idfp22',
        2 => 'idfp32'
    }
}

And i'm trying to sort it by the hash with a key of 0. In the first hash (0), there are k-v pairs of number to identifier.

In every subsequent hash (1 and 2), 0 points to the 0 from the first hash, 1 points to the 1 from the first hash, etc.

In each hash after 0 (1 and 2), there are IDs (id for person 1) that belong to p1 (person 1).

I've tried to sort this by creating a new hash with only the first hash in the one above to no avail. This is my attempt. The keys are correct but it's pointing to nil, when it should be pointing to the hash with each person's id.

    ids = {}
    org = {}

    mm[0].each do |id, name|
        ids[id] = name
    end

    mm.drop(1).each do |one|
        one.each do |key, id|
            org[ids[key]] = id
        end
    end

How can I achieve this in Ruby?

Edit:

In case the explanation doesn't suffice, here is the desired result:

org = {
  'p1' => {
    0 => 'idfp1',
    1 => 'idfp12'
  },
  'p2' => {
    0 => 'idfp2',
    1 => 'idfp22'
  },
  'p3' => {
    0 => 'idfp3',
    1 => 'idfp32'
  }
}

Upvotes: 1

Views: 1103

Answers (1)

Cary Swoveland
Cary Swoveland

Reputation: 110675

Two ways:

#1

Code

mm[0].invert.each_with_object({}) { |(k,i),h|
  h[k] = (1...mm.size).each_with_object ({}) { |j,g| g[j] = mm[j][i] } } 
  #=> {"p1"=>{1=>"idfp1", 2=>"idfp12"},
  #    "p2"=>{1=>"idfp2", 2=>"idfp22"},
  #    "p3"=>{1=>"idfp3", 2=>"idfp32"}}

Explanation

a = mm[0]
  #=> {0=>"p1", 1=>"p2", 2=>"p3"}
b = a.invert
  #=> {"p1"=>0, "p2"=>1, "p3"=>2}
b.each_with_object({}) { |(k,i),h|
  h[k] = (1...mm.size).each_with_object ({}) { |j,g| g[j] = mm[j][i] } } 
  #=> {"p1"=>{1=>"idfp1", 2=>"idfp12"},
  #    "p2"=>{1=>"idfp2", 2=>"idfp22"},
  #    "p3"=>{1=>"idfp3", 2=>"idfp32"}}

#2

Code

mm.values
  .map(&:values)
  .transpose
  .each_with_object({}) { |a,h| h[a.shift] = Hash[[*(0...a.size)].zip(a) ] }
  #=> {"p1"=>{0=>"idfp1", 1=>"idfp12"},
  #    "p2"=>{0=>"idfp2", 1=>"idfp22"},
  #    "p3"=>{0=>"idfp3", 1=>"idfp32"}} 

Explanation

a = mm.values
  #=> [{0=>"p1",     1=>"p2",     2=>"p3"    },
  #    {0=>"idfp1",  1=>"idfp2",  2=>"idfp3" },
  #    {0=>"idfp12", 1=>"idfp22", 2=>"idfp32"}] 
b = a.map(&:values
  #=> [[   "p1",        "p2",        "p3"    ],
  #    [   "idfp1",     "idfp2",     "idfp3" ],
  #    [   "idfp12",    "idfp22",    "idfp32"]] 
c = b.transpose
  #=> [["p1", "idfp1", "idfp12"],
  #    ["p2", "idfp2", "idfp22"],
  #    ["p3", "idfp3", "idfp32"]]
c.each_with_object({}) { |a,h| h[a.shift] = Hash[[*(0...a.size)].zip(a) ] }
  #=> {"p1"=>{0=>"idfp1", 1=>"idfp12"},
  #    "p2"=>{0=>"idfp2", 1=>"idfp22"},
  #    "p3"=>{0=>"idfp3", 1=>"idfp32"}} 

Upvotes: 2

Related Questions