Преобразовать декартово произведение во вложенный хеш в ruby - PullRequest
1 голос
/ 04 августа 2011

У меня есть структура с декартовым произведением, которая выглядит следующим образом (и может выйти на любую глубину) ...

variables = ["var1","var2",...]
myhash = {
   {"var1"=>"a", "var2"=>"a", ...}=>1,
   {"var1"=>"a", "var2"=>"b", ...}=>2,
   {"var1"=>"b", "var2"=>"a", ...}=>3,
   {"var1"=>"b", "var2"=>"b", ...}=>4,
}

... он имеет фиксированную структуру, но я бы хотел простую индексацию, поэтому я пытаюсь написать метод для преобразования его в это:

nested = {
   "a"=> { 
     "a"=> 1,
     "b"=> 2
    },
   "b"=> { 
     "a"=> 3,
     "b"=> 4
         }
    }

Какие-нибудь умные идеи (которые допускают произвольную глубину)?

Ответы [ 3 ]

1 голос
/ 04 августа 2011

Может быть так (не самым чистым способом):

def cartesian_to_map(myhash)
  {}.tap do |hash|
    myhash.each do |h|
      (hash[h[0]["var1"]] ||= {}).merge!({h[0]["var2"] => h[1]})
    end
  end
end

Результат:

puts cartesian_to_map(myhash).inspect
{"a"=>{"a"=>1, "b"=>2}, "b"=>{"a"=>3, "b"=>4}}
0 голосов
/ 05 августа 2011

Вот мой пример.

Он использует метод index(hash, fields), который принимает хеш и поля, по которым вы хотите проиндексировать.

Он грязный и использует локальную переменную дляувеличьте текущий уровень в индексе.

Могу поспорить, что вы можете сделать его намного приятнее.

def index(hash, fields)
  # store the last index of the fields
  last_field = fields.length - 1

  # our indexed version
  indexed = {}

  hash.each do |key, value|
    # our current point in the indexed hash
    point = indexed
    fields.each_with_index do |field, i|
      key_field = key[field]
      if i == last_field
        point[key_field] = value
      else
        # ensure the next point is a hash
        point[key_field] ||= {}
        # move our point up
        point = point[key_field]
      end
    end
  end
  # return our indexed hash
  indexed
end

Затем вы можете просто позвонить

index(myhash, ["var1", "var2"])

похоже, что вы хотите

index({
  {"var1"=>"a", "var2"=>"a"} => 1,
  {"var1"=>"a", "var2"=>"b"} => 2,
  {"var1"=>"b", "var2"=>"a"} => 3,
  {"var1"=>"b", "var2"=>"b"} => 4,
}, ["var1", "var2"])

==

{
  "a"=> { 
    "a"=> 1,
    "b"=> 2
  },
  "b"=> { 
    "a"=> 3,
    "b"=> 4
  }
}

Кажется, работает.(видеть это как сущность https://gist.github.com/1126580)

0 голосов
/ 05 августа 2011

Вот уродливое, но эффективное решение:

nested = Hash[ myhash.group_by{ |h,n| h["var1"] } ].tap{ |nested|
  nested.each do |v1,a|
    nested[v1] = a.group_by{ |h,n| h["var2"] }
    nested[v1].each{ |v2,a| nested[v1][v2] = a.flatten.last }
  end
}

p nested
#=> {"a"=>{"a"=>1, "b"=>2}, "b"=>{"a"=>3, "b"=>4}}

Вы можете рассмотреть альтернативное представление, которое проще сопоставить и (IMO) так же легко индексировать:

paired = Hash[ myhash.map{ |h,n| [ [h["var1"],h["var2"]], n ] } ]

p paired
#=> {["a", "a"]=>1, ["a", "b"]=>2, ["b", "a"]=>3, ["b", "b"]=>4}

p paired[["a","b"]]
#=> 2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...