Como obter uma saída específica repetindo um hash no Ruby?

218

Eu quero obter uma saída específica iterando um Ruby Hash.

Este é o Hash sobre o qual quero iterar:

hash = {
  1 => ['a', 'b'], 
  2 => ['c'], 
  3 => ['d', 'e', 'f', 'g'], 
  4 => ['h']
}

Esta é a saída que eu gostaria de obter:

1-----

a

b

2-----

c

3-----

d 

e

f

g

4-----

h

Em Ruby, como posso obter uma saída desse tipo com meu Hash?

sts
fonte
3
Se você está repetindo um hash e esperando para ser ordenado, você provavelmente precisará usar algum outro tipo de coleção
Allen Rice
posso passar os valores de hash como opção de botão de opção?
Sts
estou passando o hash como opção de botão de opção .. mas para a primeira opção estou obtendo o botão de opção, para outros valores não estou obtendo.
Sts
1
@Allen: Hashes são solicitados no Ruby 1.9. O Rails também fornece um OrderedHash (que ele usa apenas com moderação) se você estiver no Ruby <1.9. Veja culann.com/2008/01/rails-goodies-activesupportorderedhash
James A. Rosen

Respostas:

323
hash.each do |key, array|
  puts "#{key}-----"
  puts array
end

Em relação à ordem, devo acrescentar que em 1,8 os itens serão iterados em ordem aleatória (bem, na verdade, em uma ordem definida pela função de hash de Fixnum), enquanto em 1,9 serão iterados na ordem do literal.

sepp2k
fonte
1
aqui e se a chave não for usada em nenhum lugar? . precisamos colocar um ?lugar de chave? ex: |?, array|essa sintaxe é válida?
Huzefa biyawarwala
1
@huzefabiyawarwala Não, ?não é um nome de variável válido no Ruby. Você pode usar _, mas não precisa .
sepp2k
2
@huzefabiyawarwala Sim, você pode escrever|_, v|
sepp2k
1
o que usa array de nome de variável em vez de v ou valor?
jrhicks
1
@jrhicks Porque o OP tem um hash cujos valores são matrizes.
Radon Rosborough
85

A maneira mais básica de iterar sobre um hash é a seguinte:

hash.each do |key, value|
  puts key
  puts value
end
tomascharad
fonte
Sim, isso faz sentido. Por que a resposta de @ sepp2k tem um # {} na tecla?
committedandroider
oh não importa. Vi que era para interpolação de string
committedandroider
48
hash.keys.sort.each do |key|
  puts "#{key}-----"
  hash[key].each { |val| puts val }
end
erik
fonte
18

A classificação de chamada em um hash converte-a em matrizes aninhadas e as classifica por chave, então tudo que você precisa é o seguinte:

puts h.sort.map {|k,v| ["#{k}----"] + v}

E se você realmente não precisa da parte "----", pode ser apenas:

puts h.sort
glenn mcdonald
fonte
As chaves de hash são números, então '[k + "----"]' gera um TypeError (String não pode ser coagido no Fixnum). Você precisa '[k.to_s + "----"]'
glenn jackman
É verdade. Eu tinha cartas na minha versão de teste. Corrigido, usando o "# {k} ----" ainda melhor.
9139 glenn mcdonald
10

Minha solução de uma linha:

hash.each { |key, array| puts "#{key}-----", array }

Eu acho que é bem fácil de ler.

Elie Teyssedou
fonte
1

Você também pode refinar Hash::each para oferecer suporte à enumeração recursiva . Aqui está minha versão do Hash::each( Hash::each_pair) com bloco suporte a e enumerador :

module HashRecursive
    refine Hash do
        def each(recursive=false, &block)
            if recursive
                Enumerator.new do |yielder|
                    self.map do |key, value|
                        value.each(recursive=true).map{ |key_next, value_next| yielder << [[key, key_next].flatten, value_next] } if value.is_a?(Hash)
                        yielder << [[key], value]
                    end
                end.entries.each(&block)
            else
                super(&block)
            end
        end
        alias_method(:each_pair, :each)
    end
end

using HashRecursive

Aqui estão exemplos de uso deHash::each com e sem recursivesinalizador:

hash = {
    :a => {
        :b => {
            :c => 1,
            :d => [2, 3, 4]
        },
        :e => 5
    },
    :f => 6
}

p hash.each, hash.each {}, hash.each.size
# #<Enumerator: {:a=>{:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}, :f=>6}:each>
# {:a=>{:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}, :f=>6}
# 2

p hash.each(true), hash.each(true) {}, hash.each(true).size
# #<Enumerator: [[[:a, :b, :c], 1], [[:a, :b, :d], [2, 3, 4]], [[:a, :b], {:c=>1, :d=>[2, 3, 4]}], [[:a, :e], 5], [[:a], {:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}], [[:f], 6]]:each>
# [[[:a, :b, :c], 1], [[:a, :b, :d], [2, 3, 4]], [[:a, :b], {:c=>1, :d=>[2, 3, 4]}], [[:a, :e], 5], [[:a], {:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}], [[:f], 6]]
# 6

hash.each do |key, value|
    puts "#{key} => #{value}"
end
# a => {:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}
# f => 6

hash.each(true) do |key, value|
    puts "#{key} => #{value}"
end
# [:a, :b, :c] => 1
# [:a, :b, :d] => [2, 3, 4]
# [:a, :b] => {:c=>1, :d=>[2, 3, 4]}
# [:a, :e] => 5
# [:a] => {:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}
# [:f] => 6

hash.each_pair(recursive=true) do |key, value|
    puts "#{key} => #{value}" unless value.is_a?(Hash)
end
# [:a, :b, :c] => 1
# [:a, :b, :d] => [2, 3, 4]
# [:a, :e] => 5
# [:f] => 6

Aqui está um exemplo da própria pergunta:

hash = {
    1   =>  ["a", "b"], 
    2   =>  ["c"], 
    3   =>  ["a", "d", "f", "g"], 
    4   =>  ["q"]
}

hash.each(recursive=false) do |key, value|
    puts "#{key} => #{value}"
end
# 1 => ["a", "b"]
# 2 => ["c"]
# 3 => ["a", "d", "f", "g"]
# 4 => ["q"]

Também dê uma olhada na minha versão recursiva de Hash::merge( Hash::merge!) aqui .

MOPO3OB
fonte