Найти все значения, соответствующие параметру, во вложенном хэше ruby - PullRequest
0 голосов
/ 18 апреля 2019

Если у меня есть хэш с несколькими вложениями, например,

{
  "Monday"=>{
    "North"=>{
      "Group 1"=>[
        {:name=>"Event A", :type=>"Private"},
        {:name=>"Event B", :type=>"Public"},
      ]
    },
    "South"=>{
      "Group 1"=>[
        {:name=>"Event c", :type=>"Private"},
        {:name=>"Event D", :type=>"Public"},
        {:name=>"Event E", :type=>"Private"},
      ]
    }
  },
  "Tuesday"=>{
    "North"=>{
      "Group 1"=>[
        {:name=>"Event F", :type=>"Private"},
        {:name=>"Event G", :type=>"Public"},
      ]
    },
    "South"=>{
      "Group 1"=>[
        {:name=>"Event H", :type=>"Private"},
      ]
    }
  }
}

Я хотел бы иметь возможность искать в хэше все события с type, равным Private

Как бы я поступил так, не зная точно, какие значения ключей будут в хэше?

Ответы [ 3 ]

1 голос
/ 18 апреля 2019

Если опция gem является опцией, существует iteraptor, которая явно касается итерации глубоко вложенных структур.

Предполагая, что ваш оригинальный хеш имеет имя hash, мы идем:

hash.iteraptor.
     each(full_parent: true, yield_all: true).
     with_object({}) do |(parent, (k, v)), acc|
  (acc[parent[0...-1]] ||= []) << k if
    parent.last.is_a?(Integer) && v.nil? && k.is_a?(Hash) && k[:type] == "Private"
end

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

#⇒ {["Monday", "North", "Group 1"] =>
#       [{:name=>"Event A", :type=>"Private"}],
#   ["Monday", "South", "Group 1"] =>
#       [{:name=>"Event c", :type=>"Private"},
#        {:name=>"Event E", :type=>"Private"}],
#   ["Tuesday", "North", "Group 1"] =>
#       [{:name=>"Event F", :type=>"Private"}],
#   ["Tuesday", "South", "Group 1"] =>
#       [{:name=>"Event H", :type=>"Private"}]}
1 голос
/ 18 апреля 2019

При рекурсивном решении я сделал три предположения:

  • Может быть любое количество вложенных массивов и хэшей;
  • :type - единственный известный ключ;
  • если хеш содержит ключ :type, он содержит ровно еще один ключ.

def get_em(obj)
  arr = []
  case obj
  when Hash
    obj.values.each do |v|
      case v
      when "Private"
        arr += obj.values-[v]
      when Hash, Array
        arr += get_em(v)
      end
    end
  when Array
    obj.each { |e| arr += get_em(e) if Hash === e || Array === e }
  end
  arr
end

Если h являетсяхэш, приведенный в примере,

get_em(h)
  #=> ["Event A", "Event C", "Event E", "Event F", "Event H"]

Примечание Hash === e эквивалентно e.is_a?(Hash).

0 голосов
/ 18 апреля 2019

Попробуйте эту рекурсию:

  def hash_match(the_hash)
    found=false
    the_hash.each do |key, value|
      if value.is_a?(Hash)
        if hash_match(value)
          if value.has_key :name
            puts value[:name]
          end
        end
      elsif value.is_a?(Array)
        value.each do |element|
          if element.is_a?(Hash)
            if hash_match(element)
              if element.has_key? :name
                puts element[:name]
              end
            end
          end
        end
      else
        if key==:type && value=="Private"
          found=true
        end
      end
    end
    return found
  end

Тогда просто позвоните hash_match(your_hash)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...