Esclareça se você deseja alterar os objetos da string original, apenas modifique o original possui ou modifique nada.
Phrogz
1
Então você aceitou a resposta errada. (Sem ofensa para @pst destina, como eu, pessoalmente, também defendem a programação em vez de mutação objetos de estilo funcional.)
Phrogz
Mas ainda assim a abordagem foi boa soo
theReverseFlick 4/11/11
Respostas:
178
Se você deseja que as próprias seqüências de caracteres sejam alteradas no lugar (afetando de forma possível e desejável outras referências aos mesmos objetos de sequência de caracteres):
# Two ways to achieve the same result (any Ruby version)
my_hash.each{|_,str| str.gsub!/^|$/,'%'}
my_hash.each{|_,str| str.replace "%#{str}%"}
Se você deseja que o hash mude no lugar, mas não deseja afetar as strings (deseja que ele obtenha novas strings):
# Two ways to achieve the same result (any Ruby version)
my_hash.each{|key,str| my_hash[key]="%#{str}%"}
my_hash.inject(my_hash){|h,(k,str)| h[k]="%#{str}%"; h }
@ Andrew Marshall Você está certo, obrigado. No Ruby 1.8, Hash.[]não aceita uma matriz de pares de matrizes, requer um número par de argumentos diretos (daí a divisão inicial).
Phrogz
2
Na verdade, o Hash. [Key_value_pairs] foi introduzido no 1.8.7, então apenas o Ruby 1.8.6 não precisa do splat & flatten.
Marc-André Lafortune
2
@Aupajo Hash#eachproduz a chave e o valor para o bloco. Nesse caso, eu não me importei com a chave e, portanto, não nomeei nada útil. Os nomes de variáveis podem começar com um sublinhado e, de fato, podem ser apenas um sublinhado. Não há benefício de desempenho em fazer isso, é apenas uma observação sutil de auto-documentação de que não estou fazendo nada com esse primeiro valor de bloco.
Phrogz 01/09/11
1
Eu acho que você quer dizer my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }, tem que retornar o hash do bloco
aceofspades
1
Como alternativa, você pode usar o método each_value, que é um pouco mais fácil de entender do que usar um sublinhado para o valor da chave não utilizada.
Obrigado, era realmente o que eu estava procurando. Não sei por que você não é tão votado.
simperreault
7
Isso é muito lento e com muita memória RAM. O Hash de entrada é iterado para produzir um conjunto intermediário de matrizes aninhadas que são convertidas em um novo Hash. Ignorando o pico de uso da RAM, o tempo de execução é muito pior - comparando isso com as soluções de modificação no local em outra resposta, mostra 2,5s versus 1,5s no mesmo número de iterações. Desde Ruby é uma linguagem relativamente lento, evitando os bits lentos da língua lenta faz muito sentido :-)
Andrew Hodgkinson
2
@AndrewHodgkinson, em geral, concordo e não estou advogando não prestar atenção ao desempenho em tempo de execução, não acompanhar todas essas armadilhas de desempenho começa a se tornar uma dor e contraria a filosofia do Ruby? Eu acho que isso é menos um comentário para você e mais um comentário geral sobre o eventual paradoxo que isso nos leva a usar ruby.
Elsurudo 22/05/19
4
O enigma é: bem, já estamos desistindo do desempenho em nossa decisão de usar ruby, então que diferença faz "esse outro pouquinho"? É uma ladeira escorregadia, não é? Para constar, prefiro esta solução à resposta aceita, de uma perspectiva de legibilidade.
Não é exatamente verdade: novas cadeias são alocadas. Ainda assim, uma solução interessante que é eficaz. +1
Phrogz
@Phrogz bom ponto; Eu atualizei a resposta. A alocação de valor não pode ser evitada em geral porque nem todas as transformações de valor podem ser expressas como mutadores como gsub!.
Sim
3
Igual à minha resposta, mas com outro sinônimo, concordo que updatetransmite a intenção melhor que merge!. Eu acho que essa é a melhor resposta.
1
Se você não usar k, use _.
sekrett
28
Um pouco mais legível, mappara uma matriz de hashes de elemento único e reduceque commerge
Mais clara de usarHash[the_hash.map { |key,value| [key, "%#{value}%"] }]
meagar
Essa é uma maneira extremamente ineficiente de atualizar valores. Para cada par de valores, ele cria primeiro um par Array(para map) e depois a Hash. Em seguida, cada etapa da operação de redução duplicará o "memorando" Hashe adicionará o novo par de valores-chave. No uso menos :merge!em reducemodificar a final Hashno lugar. E, no final, você não está modificando os valores do objeto existente, mas criando um novo objeto, que não é o que a pergunta foi feita.
Um método que não apresenta efeitos colaterais ao original:
h ={:a =>'a',:b =>'b'}
h2 =Hash[h.map {|k,v|[k,'%'+ v +'%']}]
O mapa Hash # também pode ser uma leitura interessante, pois explica por Hash.mapque não retorna um Hash (e é por isso que a matriz de [key,value]pares resultante é convertida em um novo Hash) e fornece abordagens alternativas para o mesmo padrão geral.
Feliz codificação.
[Isenção de responsabilidade: não tenho certeza se a Hash.mapsemântica muda no Ruby 2.x]
Não gosto de efeitos colaterais, mas +1 na abordagem :) Existe each_with_objectno Ruby 1.9 (IIRC) que evita a necessidade de acessar o nome diretamente e Map#mergetambém pode funcionar. Não tenho certeza de como os detalhes complexos diferem.
1
O hash inicial é modificado - tudo bem se o comportamento for antecipado, mas pode causar problemas sutis se "esquecido". Prefiro reduzir a mutabilidade do objeto, mas nem sempre isso é prático. (Ruby é praticamente uma linguagem "side-livre de efeito" ;-)
8
Hash.merge! é a solução mais limpa
o ={ a:'a', b:'b'}
o.merge!(o){|key, value|"%#{ value }%"}
puts o.inspect
>{:a =>"%a%",:b =>"%b%"}
Respostas:
Se você deseja que as próprias seqüências de caracteres sejam alteradas no lugar (afetando de forma possível e desejável outras referências aos mesmos objetos de sequência de caracteres):
Se você deseja que o hash mude no lugar, mas não deseja afetar as strings (deseja que ele obtenha novas strings):
Se você deseja um novo hash:
fonte
Hash.[]
não aceita uma matriz de pares de matrizes, requer um número par de argumentos diretos (daí a divisão inicial).Hash#each
produz a chave e o valor para o bloco. Nesse caso, eu não me importei com a chave e, portanto, não nomeei nada útil. Os nomes de variáveis podem começar com um sublinhado e, de fato, podem ser apenas um sublinhado. Não há benefício de desempenho em fazer isso, é apenas uma observação sutil de auto-documentação de que não estou fazendo nada com esse primeiro valor de bloco.my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }
, tem que retornar o hash do blocoNo Ruby 2.1 e superior, você pode fazer
fonte
#transform_values!
como indicado por sschmeck ( stackoverflow.com/a/41508214/6451879 ).O Ruby 2.4 introduziu o método
Hash#transform_values!
, que você pode usar.fonte
Hash#transform_values
(sem o estrondo), que não modifica o receptor. Caso contrário, uma ótima resposta, obrigado!reduce
:-pA melhor maneira de modificar os valores de um Hash é
Menos código e intenção clara. Também mais rápido porque nenhum novo objeto é alocado além dos valores que devem ser alterados.
fonte
gsub!
.update
transmite a intenção melhor quemerge!
. Eu acho que essa é a melhor resposta.k
, use_
.Um pouco mais legível,
map
para uma matriz de hashes de elemento único ereduce
que commerge
fonte
Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
Array
(paramap
) e depois aHash
. Em seguida, cada etapa da operação de redução duplicará o "memorando"Hash
e adicionará o novo par de valores-chave. No uso menos:merge!
emreduce
modificar a finalHash
no lugar. E, no final, você não está modificando os valores do objeto existente, mas criando um novo objeto, que não é o que a pergunta foi feita.nil
sethe_hash
estiver vazioExiste um novo método 'Rails way' para esta tarefa :) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values
fonte
Hash#transform_values
. Este deve ser o caminho a seguir a partir de agora.Um método que não apresenta efeitos colaterais ao original:
O mapa Hash # também pode ser uma leitura interessante, pois explica por
Hash.map
que não retorna um Hash (e é por isso que a matriz de[key,value]
pares resultante é convertida em um novo Hash) e fornece abordagens alternativas para o mesmo padrão geral.Feliz codificação.
[Isenção de responsabilidade: não tenho certeza se a
Hash.map
semântica muda no Ruby 2.x]fonte
Hash.map
semântica muda no Ruby 2.x?fonte
each_with_object
no Ruby 1.9 (IIRC) que evita a necessidade de acessar o nome diretamente eMap#merge
também pode funcionar. Não tenho certeza de como os detalhes complexos diferem.Hash.merge! é a solução mais limpa
fonte
Depois de testá-lo com o RSpec assim:
Você pode implementar o Hash # map_values da seguinte maneira:
A função então pode ser usada assim:
fonte
Se você está curioso para saber qual é a variante mais rápida aqui:
fonte