Como substituir uma chave de hash por outra chave

191

Eu tenho uma condição em que, recebo um hash

  hash = {"_id"=>"4de7140772f8be03da000018", .....}

e eu quero esse hash como

  hash = {"id"=>"4de7140772f8be03da000018", ......}

PS : Não sei quais são as chaves no hash, elas são aleatórias, que vêm com um prefixo "_" para cada chave e não quero sublinhados

Manish Das
fonte
Isso pode ajudá-lo: stackoverflow.com/questions/4044451/…
corroído
+1 para uma pergunta útil
ashisrai_
@ a5his: Estou feliz que foi de ajuda :)
Manish Das

Respostas:

710
hash[:new_key] = hash.delete :old_key
gayavat
fonte
8
Me salvou um casal LOC, adorei!
Nicohvi 15/06
10
Geralmente não gosto de código ruby ​​"inteligente" porque leva algum tempo para dizer o que está realmente fazendo. Por outro lado, sua solução é simples e descritiva.
Lucas
3
Esta deve ser de fato a resposta aceita! Fácil, limpo e direto ao ponto!
GigaBass 02/04
1
Essa resposta é elegante, mas na verdade não responde à pergunta. A publicação afirma que as chaves que precisam ser substituídas são desconhecidas ... Só sabemos que elas começam com um sublinhado, não sabemos o que realmente são as chaves.
Dsel
2
portanto, isso cria um novo par de chave / valor em que você especifica a nova chave e obtém o valor do que hash.delete :old_keyretorna e a exclusão usa a chave antiga. WOW, eu quero isso tatuado Graças algum lugar :-D
Bart C
136

trilhos O ​​Hash possui um método padrão:

hash.transform_keys{ |key| key.to_s.upcase }

http://api.rubyonrails.org/classes/Hash.html#method-i-transform_keys

UPD: método ruby ​​2.5

gayavat
fonte
4
É o método Rails, não padrão. Boa resposta embora.
user2422869
1
Além disso, esse método não pode operar com chaves de hash recursivamente.
Sergio Belevskij 30/11/2015
5
deep_transform_keys pode ser usado para isso :) apidock.com/rails/v4.2.1/Hash/deep_transform_keys
gayavat
1
Finalmente! Isto é exatamente o que eu procurei!
precisa saber é o seguinte
4
Esta é uma parte normal da linguagem como Ruby 2.5: docs.ruby-lang.org/en/trunk/Hash.html#method-i-transform_keys
David Grayson
39

Se todas as chaves forem cadeias de caracteres e todas elas tiverem o prefixo de sublinhado, você poderá corrigir o hash no lugar com isso:

h.keys.each { |k| h[k[1, k.length - 1]] = h[k]; h.delete(k) }

O k[1, k.length - 1]bit agarra todos, kexceto o primeiro caractere. Se você deseja uma cópia, então:

new_h = Hash[h.map { |k, v| [k[1, k.length - 1], v] }]

Ou

new_h = h.inject({ }) { |x, (k,v)| x[k[1, k.length - 1]] = v; x }

Você também pode usar subse não gostar da k[]notação para extrair uma substring:

h.keys.each { |k| h[k.sub(/\A_/, '')] = h[k]; h.delete(k) }
Hash[h.map { |k, v| [k.sub(/\A_/, ''), v] }]
h.inject({ }) { |x, (k,v)| x[k.sub(/\A_/, '')] = v; x }

E, se apenas algumas das chaves tiverem o prefixo de sublinhado:

h.keys.each do |k|
  if(k[0,1] == '_')
    h[k[1, k.length - 1]] = h[k]
    h.delete(k)
  end
end

Modificações semelhantes podem ser feitas em todas as outras variantes acima, mas estas duas:

Hash[h.map { |k, v| [k.sub(/\A_/, ''), v] }]
h.inject({ }) { |x, (k,v)| x[k.sub(/\A_/, '')] = v; x }

deve estar bem com chaves que não tenham prefixos de sublinhado sem modificações extras.

mu é muito curto
fonte
seu answer funcionou, mas depois ala i encontrados alguns de hash como este
Manish Das
3
{"_id" => "4de7140772f8be03da000018", "_type" => "Estação de trabalho", "created_at" => "2011-06-02T10: 24: 35 + 05: 45", "input_header_ids" => [], "line_id "=>" 4de7140472f8be03da000017 "," updated_at "=>" 2011-06-02T10: 24: 35 + 05: 45 "}
Manish Das
2
{"id" => "4de7140772f8be03da000018", "type" => "WorkStation", "reated_at" => "2011-06-02T10: 24: 35 + 05: 45", "nput_header_ids" => [], "ine_id "=>" 4de7140472f8be03da000017 "," pdated_at "=>" 2011-06-02T10: 24: 35 + 05: 45 "}
Manish Das
@ Manish: Eu disse "Se todas as chaves são strings e todas elas têm o prefixo de sublinhado". Incluí um exemplo de abordagem para suas "chaves sem prefixos de sublinhado" em uma atualização.
mu é muito curto
2
@ Manish: "k" é para "chave", "v" é para "valor", "x" é para "Eu não sei como chamá-lo, mas fui treinado como matemático, então chamo de x".
mu é muito curto
14

você pode fazer

hash.inject({}){|option, (k,v) | option["id"] = v if k == "_id"; option}

Isso deve funcionar para o seu caso!

Sadiksha Gautam
fonte
11

Se quisermos renomear uma chave específica no hash, podemos fazer o seguinte:
Suponha que meu hash seja my_hash = {'test' => 'ruby hash demo'}
Agora, quero substituir 'test' por 'message';
my_hash['message'] = my_hash.delete('test')

Swapnil Chincholkar
fonte
Como é a sua resposta é a solução para o meu problema? Se você achou que isso era útil, poderia ter adicionado o comentário na pergunta. Minha pergunta não era substituir uma chave por outra, a solução que você deu é uma propriedade Hash muito básica. no meu caso hash[:new_key] = has[:old_key], não é:, em vez disso é hash[:dynamic_key] = hash[:_dynamic_key]:, era uma pergunta clara sobre regex e não simples substituição de hash.
Manish Das
2
Cheguei a isso através de uma pesquisa no google e queria a resposta do @ Swapnil. Obrigado
toobulkeh
10
h.inject({}) { |m, (k,v)| m[k.sub(/^_/,'')] = v; m }
DigitalRoss
fonte
4
Eu gosto que você tentou usar um regex para filtrar os sublinhados corretamente, mas você deve estar ciente de que, em ruby, diferentemente do javascript e de outros, / ^ / significa 'start of string OR LINE' e / $ / means 'end of string OR LINE '. É improvável que as chaves possuam novas linhas nelas neste caso, mas você deve estar ciente de que o uso desses dois operadores em ruby ​​não é apenas propenso a erros, mas também muito perigoso quando usado incorretamente nas validações contra injeções. Veja aqui uma explicação. Espero que você não se importe de espalhar a consciência.
Jorn van de Beek
2
hash.each {|k,v| hash.delete(k) && hash[k[1..-1]]=v if k[0,1] == '_'}
maerics
fonte
1

Eu fui exagero e criei o seguinte. Minha motivação por trás disso foi anexar chaves de hash para evitar conflitos de escopo ao mesclar / achatar hashes.

Exemplos

Estender classe hash

Adiciona o método rekey às instâncias Hash.

# Adds additional methods to Hash
class ::Hash
  # Changes the keys on a hash
  # Takes a block that passes the current key
  # Whatever the block returns becomes the new key
  # If a hash is returned for the key it will merge the current hash 
  # with the returned hash from the block. This allows for nested rekeying.
  def rekey
    self.each_with_object({}) do |(key, value), previous|
      new_key = yield(key, value)
      if new_key.is_a?(Hash)
        previous.merge!(new_key)
      else
        previous[new_key] = value
      end
    end
  end
end

Anexar Exemplo

my_feelings_about_icecreams = {
  vanilla: 'Delicious',
  chocolate: 'Too Chocolatey',
  strawberry: 'It Is Alright...'
}

my_feelings_about_icecreams.rekey { |key| "#{key}_icecream".to_sym }
# => {:vanilla_icecream=>"Delicious", :chocolate_icecream=>"Too Chocolatey", :strawberry_icecream=>"It Is Alright..."}

Exemplo de acabamento

{ _id: 1, ___something_: 'what?!' }.rekey do |key|
  trimmed = key.to_s.tr('_', '')
  trimmed.to_sym
end
# => {:id=>1, :something=>"what?!"}

Achatando e anexando um "escopo"

Se você passar um hash de volta para re-codificar, ele mesclará o hash, o que permite nivelar as coleções. Isso nos permite adicionar escopo às nossas chaves ao nivelar um hash para evitar a substituição de uma chave na mesclagem.

people = {
  bob: {
    name: 'Bob',
    toys: [
      { what: 'car', color: 'red' },
      { what: 'ball', color: 'blue' }
    ]
  },
  tom: {
    name: 'Tom',
    toys: [
      { what: 'house', color: 'blue; da ba dee da ba die' },
      { what: 'nerf gun', color: 'metallic' }
    ]
  }
}

people.rekey do |person, person_info|
  person_info.rekey do |key|
    "#{person}_#{key}".to_sym
  end
end

# =>
# {
#   :bob_name=>"Bob",
#   :bob_toys=>[
#     {:what=>"car", :color=>"red"},
#     {:what=>"ball", :color=>"blue"}
#   ],
#   :tom_name=>"Tom",
#   :tom_toys=>[
#     {:what=>"house", :color=>"blue; da ba dee da ba die"},
#     {:what=>"nerf gun", :color=>"metallic"}
#   ]
# }
CTS_AE
fonte
0

As respostas anteriores são boas o suficiente, mas podem atualizar os dados originais. Caso você não queira que os dados originais sejam afetados, tente meu código.

 newhash=hash.reject{|k| k=='_id'}.merge({id:hash['_id']})

Primeiro ele ignora a chave '_id' e depois se funde com a chave atualizada.

purushothaman poovai
fonte