Como mesclar dois hashes sem substituir chaves duplicadas no Ruby?

140

Existe uma maneira fácil ou elegante de mesclar dois hashes sem substituir chaves duplicadas?

Ou seja, se a chave estiver presente no hash original, não quero alterar seu valor.

Claudio Acciaresi
fonte
Você realmente quer dizer matrizes (por exemplo: ['a', 'b', 'c']) ou hashes (por exemplo: {'a' => 1, 'b' => 2, 'c' => 3}) ?
Alex Reisner
Desculpe, eu estava falando sobre hashes :)
Claudio Acciaresi

Respostas:

232

Se você possui dois hashes optionse defaultsdeseja unir defaults-se optionssem substituir as chaves existentes, o que realmente deseja fazer é o inverso: unir options- se a defaults:

options = defaults.merge(options)

Ou, se você estiver usando o Rails, poderá:

options.reverse_merge!(defaults)
Alex Reisner
fonte
Concordo totalmente, muito obrigado por reverse_merge! método não sabia :) :)
Claudio Acciaresi
Por que os parênteses são necessários aqui? Você não pode simplesmente fazer as opções default.merge que aparecem.
314 Donato
1
Eles são depreciativo reverse_merge!por questões de segurança em trilhos 5.1
MIRV - Matt
@ Mirv-Matt - Não vejo um aviso de depreciação. apidock.com/rails/v6.0.0/Hash/reverse_merge%21
Kshitij 14/01
17

Existe uma maneira na biblioteca Ruby padrão de mesclar Hashes sem substituir os valores existentes ou reatribuir o hash.

important_hash.merge!(defaults) { |key, important, default| important }
ujifgc
fonte
3

Se o seu problema é que o hash original e o segundo podem ter chaves duplicadas e você não deseja sobrescrever em nenhuma direção, pode ser necessário fazer uma mesclagem manual simples com algum tipo de verificação e manuseio de colisão:

hash2.each_key do |key|
  if ( hash1.has_key?(key) )
       hash1[ "hash2-originated-#{key}" ] = hash2[key]
  else
       hash1[key]=hash2[key]
  end
end

Obviamente, isso é muito rudimentar e supõe que o hash1 não possui nenhuma chave chamada "hash2-originou-seja o que for" - é melhor adicionar um número à chave para que ela se torne key1, key2 etc. um que ainda não esteja no hash1. Além disso, há alguns meses que não faço ruby, então isso provavelmente não está sintaticamente correto, mas você deve ser capaz de entender.

Como alternativa, redefina o valor da chave como uma matriz para que o hash1 [chave] retorne o valor original do hash1 e o valor do hash2. Depende do que você quer que seu resultado seja realmente.

glenatron
fonte
Que tal se não manter as duas chaves, mas somar os valores da mesma chave?
Tom KC Chiu
1
@ TomK.C.Chiu Isso dependeria muito das circunstâncias que não podemos julgar a partir da pergunta - e se os valores no hash1 forem seqüências de caracteres e hash2 forem inteiros? Em alguns casos, isso pode ser uma opção viável, mas com mais freqüência isso causaria problemas - a sugestão de usar listas para valores funciona de maneira bastante clara.
glenatron
0

Aqui você pode mesclar seu hash 2 por reverse_merge

order = {
 id: 33987,
 platform: 'web'
}

user = {
  name: 'Jhon Doe',
  email: '[email protected]' 
}
newHash = oder.reverse_merge!(user)
render json: { data: newHash, status: 200 }
Daniel Agus Sidabutar
fonte
0

Se você deseja mesclar os dois hashes optionse defaultssem substituir o hash de destino, verifique selectse a chave já está presente no hash de destino. Aqui está a solução Ruby pura sem Rails:

options  = { "a" => 100, "b" => 200 }
defaults = { "b" => 254, "c" => 300 }
options.merge!(defaults.select{ |k,_| not options.has_key? k })

# output
# => {"a"=>100, "b"=>200, "c"=>300}

Ou, se a chave estiver presente, mas contiver nile você quiser substituí-la:

options.merge!(defaults.select{ |k,_| options[k].nil? })
cristão
fonte