Ok, então aqui está o negócio, eu venho pesquisando há séculos para encontrar uma solução para isso e, embora existam muitos por aí, eles não parecem fazer o trabalho que estou procurando.
Basicamente, eu tenho uma matriz estruturada como esta
["item 1", "item 2", "item 3", "item 4"]
Eu quero converter isso em um Hash para que fique assim
{ "item 1" => "item 2", "item 3" => "item 4" }
ou seja, os itens que estão nos índices 'pares' são as chaves e os itens nos índices 'ímpares' são os valores.
Alguma idéia de como fazer isso de forma limpa? Suponho que um método de força bruta seria apenas extrair todos os índices pares em uma matriz separada e depois fazer um loop em torno deles para adicionar os valores.
*
é chamado de operador splat . Ele pega uma matriz e a converte em uma lista literal de itens. Então*[1,2,3,4]
=>1, 2, 3, 4
. Neste exemplo, o acima é equivalente a fazerHash["item 1", "item 2", "item 3", "item 4"]
. EHash
possui um[]
método que aceita uma lista de argumentos (criando chaves de índices pares e valores de índices ímpares), masHash[]
não aceita uma matriz, por isso dividimos a matriz usando*
.Hash[a.each_slice(2).to_a]
.O Ruby 2.1.0 introduziu um
to_h
método em Array que faz o que você precisa se sua matriz original consistir em matrizes de pares de valores-chave: http://www.ruby-doc.org/core-2.1.0/Array.html#method -i-to_h .fonte
bar
precisa ser um símbolo, e o símbolo:2
deve ser um número inteiro. Portanto, sua expressão corrigida éa = [[:foo, 1], [:bar, 2]]
).Basta usar
Hash.[]
com os valores na matriz. Por exemplo:fonte
*arr
convertearr
em uma lista de argumentos, então isso está chamando o[]
método de Hash com o conteúdo de arr como argumentos.Ou, se você tiver uma matriz de
[key, value]
matrizes, poderá:fonte
Hash[*arr]
{ [1, 2] => [3, 4] }
. E como o título da pergunta diz "Array to Hash" e o método{ 1 => 2, 3 => 4}.to_a # => [[1, 2], [3, 4]]
interno "Hash to Array": Na verdade, foi assim que acabei aqui de qualquer maneira.Hash[arr]
fará o trabalho para você.#inject
método. Com#merge!
,#each_with_object
deveria ter sido usado. Se#inject
é insistido, em#merge
vez de#merge!
deveria ter sido usado.Isto é o que eu estava procurando ao pesquisar isso:
[{a: 1}, {b: 2}].reduce({}) { |h, v| h.merge v } => {:a=>1, :b=>2}
fonte
merge
, ele constrói e descarta uma nova iteração de hash por loop e é muito lento. Se você tem uma variedade de hashes, tente[{a:1},{b:2}].reduce({}, :merge!)
- ele mescla tudo no mesmo (novo) hash..reduce(&:merge!)
[{a: 1}, {b: 2}].reduce(&:merge!)
avalia para{:a=>1, :b=>2}
[{a: 1}, {b: 2}].reduce(&:merge!)
é o mesmo[{a: 1}, {b: 2}].reduce { |m, x| m.merge(x) }
que[{b: 2}].reduce({a: 1}) { |m, x| m.merge(x) }
.Enumerator
incluiEnumerable
. Desde2.1
,Enumerable
também tem um método#to_h
. Por isso, podemos escrever:Porque
#each_slice
sem o bloco nos dáEnumerator
, e conforme a explicação acima, podemos chamar o#to_h
método noEnumerator
objeto.fonte
Você pode tentar assim, para matriz única
para matriz de matriz
fonte
ou, se você odeia
Hash[ ... ]
:ou, se você é um fã preguiçoso de programação funcional interrompida:
fonte
Hash[ ... ]
, mas quero usá-lo como um método encadeada (como você pode fazer comto_h
) você pode combinar sugestões Boris e de escrita:arr.each_slice( 2 ).map { |e| e }.tap { |a| break Hash[a] }
Todas as respostas assumem que a matriz inicial é única. O OP não especificou como lidar com matrizes com entradas duplicadas, o que resulta em chaves duplicadas.
Vamos olhar para:
Você perderá o
item 1 => item 2
par, pois é substituído poritem 1 => item 5
:Todos os métodos, incluindo o
reduce(&:merge!)
resultado na mesma remoção.Pode ser que isso seja exatamente o que você espera, no entanto. Mas em outros casos, você provavelmente deseja obter um resultado com um
Array
valor for:A maneira ingênua seria criar uma variável auxiliar, um hash que tenha um valor padrão e preenchê-lo em um loop:
Pode ser possível usar
assoc
ereduce
fazer acima em uma linha, mas isso se torna muito mais difícil de raciocinar e ler.fonte