Как обновить значение единственного хэша, когда ключ появляется несколько раз в ruby? - PullRequest
2 голосов
/ 24 октября 2019

У меня есть вложенный хэш, который я пытаюсь отсортировать по имени.

pigeon_data = {
  :color => {
    :purple => ["Theo", "Peter Jr.", "Lucky"],
    :grey => ["Theo", "Peter Jr.", "Ms. K"],
    :white => ["Queenie", "Andrew", "Ms. K", "Alex"],
    :brown => ["Queenie", "Alex"]

, поэтому я ищу что-то вроде

 "Theo" => {
    :color => ["purple", "grey"],
    :gender => ["male"],
    :lives => ["Subway"]
  },
  "Peter Jr." => {
    :color => ["purple", "grey"],
    :gender => ["male"],
    :lives => ["Library"]
  },

Но каждый раз, когда я пытаюсь изменитьодно из значений, которое оно заканчивает, изменяет все значения одним и тем же ключом.

{"Theo"=>
  {:color=>["purple", "grey"],
 "Peter Jr."=>
  {:color=>["purple", "grey"],
  ...    

Мои коды беспорядочные, но здесь это более ранний код, уже помещающий семь птиц во вложенный хэш в качестве ключей верхнего уровня, я думаюпроблема где-то здесь

def sort_birds(new_sort_1)
  new_sort_2 = Marshal.load(Marshal.dump(new_sort_1))
    new_sort_1.each do |ka,va|
      va.each do |kb,vb|
        vb.each do |kc,vc|
          #binding.pry
          if vc.include?("#{ka}") && new_sort_2[ka][kb].is_a?(Array)
            new_sort_2["#{ka}"][kb] << "#{kc}"
          elsif vc.include?("#{ka}")
            new_sort_2["#{ka}"][kb] = Array.new
            new_sort_2["#{ka}"][kb] << "#{kc}"
          else

Ответы [ 3 ]

2 голосов
/ 24 октября 2019

Учитывая предоставленную структуру:

pigeon_data = { :color => { 
    :purple => ["Theo", "Peter Jr.", "Lucky"], 
    :grey => ["Theo", "Peter Jr.", "Ms. K"],   
    :white => ["Queenie", "Andrew", "Ms. K", "Alex"], 
    :brown => ["Queenie", "Alex"] }, 
  :gender => {  
    :male => ["Alex", "Theo", "Peter Jr.", "Andrew", "Lucky"], 
    :female => ["Queenie", "Ms. K"] }, 
  :lives => {  
    "Subway" => ["Theo", "Queenie"], 
    "Central Park" => ["Alex", "Ms. K", "Lucky"], 
    "Library" => ["Peter Jr."], 
    "City Hall" => ["Andrew"] } }

Мы можем циклически проходить по наборам, используя хэш, который строится на основе процесса по умолчанию.

builder = Hash.new {|h,k| h[k] = Hash.new {|h2,k2| h2[k2] = []}}
pigeon_data.each_with_object(builder) do |(category,values),cage| 
  values.each do |cat_value,birds| 
    birds.each do |bird|
      cage[bird][category] << cat_value
    end  
  end 
end

Когда строитель получает новый ключ, он назначает новый хэш в качестве значения. Когда этот вложенный Hash получает новый ключ, он присваивает пустой массив в качестве значения. Итак, мы просто размещаем элементы в том порядке, в котором мы хотим, чтобы они отображались [bird][category] << value

В результате:

    {"Theo"=>{
       :color=>[:purple, :grey], 
       :gender=>[:male], 
       :lives=>["Subway"]}, 
     "Peter Jr."=>{
       :color=>[:purple, :grey], 
       :gender=>[:male], 
       :lives=>["Library"]}, 
     "Lucky"=>{
       :color=>[:purple], 
       :gender=>[:male], 
       :lives=>["Central Park"]}, 
     "Ms. K"=>{
       :color=>[:grey, :white], 
       :gender=>[:female], 
       :lives=>["Central Park"]}, 
     "Queenie"=>{
       :color=>[:white, :brown], 
       :gender=>[:female], 
       :lives=>["Subway"]}, 
     "Andrew"=>{
       :color=>[:white], 
       :gender=>[:male], 
       :lives=>["City Hall"]}, 
     "Alex"=>{
       :color=>[:white, :brown],
       :gender=>[:male], 
       :lives=>["Central Park"]}} 
1 голос
/ 24 октября 2019

То, что вы захотите сделать, - это создать совершенно новую структуру вместо того, чтобы пытаться преобразовать копию существующей структуры в совершенно новую форму. Руби - это все о преобразованиях, определенных как серия новых объектов, в отличие от модификаций того же объекта на месте.

Это можно проиллюстрировать следующим образом:

def repigeonize(data)
  # Create a target structure for this data that's a Hash with a default...
  result = Hash.new do |h,k|
    # ...inner hash that has...
    h[k] = Hash.new do |ih, ik|
      # ... arrays assigned by default to its keys.
      ih[ik] = [ ]
    end
  end

  # Iterate over the data starting at the top level where attributes...
  data.each do |attr, set|
    # ...have keys that represent values...
    set.each do |value, names|
      # ...and list the names of those with those properties.
      names.each do |name|
        result[name][attr] << value.to_s # Converted to a string.
      end
    end
  end

  # Pass the result back
  result
end

Где это работает так:

pigeon_data = {
  color: {
    purple: ["Theo", "Peter Jr.", "Lucky"],
    grey: ["Theo", "Peter Jr.", "Ms. K"],
    white: ["Queenie", "Andrew", "Ms. K", "Alex"],
    brown: ["Queenie", "Alex"]
  },
  lives: {
    library: ["Peter Jr."],
    cellar: ["Queenie","Alex"],
    attic: ["Lucky","Ms. K"]
  }
}

p repigeonize(pigeon_data)
# => {"Theo"=>{:color=>["purple", "grey"]}, "Peter Jr."=>{:color=>["purple", "grey"], :lives=>["library"]}, "Lucky"=>{:color=>["purple"], :lives=>["attic"]}, "Ms. K"=>{:color=>["grey", "white"], :lives=>["attic"]}, "Queenie"=>{:color=>["white", "brown"], :lives=>["cellar"]}, "Andrew"=>{:color=>["white"]}, "Alex"=>{:color=>["white", "brown"], :lives=>["cellar"]}}
0 голосов
/ 24 октября 2019
{ color: pigeon_data[:color].flat_map { |k,v| [k].product(v) }.
  each_with_object({}) { |(color,bird),h| (h[bird] ||= []) << color.to_s } }
  #=> {:color=>{"Theo"=>["purple", "grey"],
  #             "Peter Jr."=>["purple", "grey"],
  #             "Lucky"=>["purple"],
  #             "Ms. K"=>["grey, "white"],
  #             "Queenie"=>["white, "brown"],
  #             "Andrew"=>["white"],
  #             "Alex"=>["white", "brown"]}} 

Шаги следующие:

a = pigeon_data[:color].flat_map { |k,v| [k].product(v) }
  #=> [[:purple, "Theo"], [:purple, "Peter Jr."], [:purple, "Lucky"],
  #    [:grey, "Theo"], [:grey, "Peter Jr."], [:grey, "Ms. K"],
  #    [:white, "Queenie"], [:white, "Andrew"], [:white, "Ms. K"],
  #    [:white, "Alex"], [:brown, "Queenie"], [:brown, "Alex"]] 
b = a.each_with_object({}) { |(color,bird),h| (h[bird] ||= []) << color.to_s }
  #=> {"Theo"=>["purple", "grey"], "Peter Jr."=>["purple", "grey"],
  #    "Lucky"=>["purple"], "Ms. K"=>["grey", "white"], "Queenie"=>["white", "brown"],
  #    "Andrew"=>["white"], "Alex"=>["white", "brown"]} 
{ color: b }
  #=> <as above>

В качестве альтернативы можно заменить each_with_object({}) на each_with_object(Hash.new { |h,k| h[k] = [] }) и (h[bird] ||= []) на h[bird].

См. Enumerable # flat_map и Array # product .

...