Cómo reemplazar una clave hash con otra clave

191

Tengo una condición en la que tengo un hash

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

y quiero este hash como

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

PD : No sé cuáles son las claves en el hash, son aleatorias, lo que viene con un prefijo "_" para cada clave y no quiero subrayados

Manish Das
fuente
Esto podría ayudarlo: stackoverflow.com/questions/4044451/…
corroído el
+1 para preguntas útiles
ashisrai_
@ a5his: Me alegra que haya sido de ayuda :)
Manish Das

Respuestas:

710
hash[:new_key] = hash.delete :old_key
gayavat
fuente
8
Me salvó un par de LOC, ¡me encanta!
nicohvi
10
A menudo no me gusta el código ruby ​​"inteligente" porque lleva algún tiempo decir lo que realmente está haciendo. Su solución es, por otro lado, simple y descriptiva.
Lucas
3
¡Esta debería ser la respuesta aceptada! ¡Fácil, limpio y directo al grano!
GigaBass
1
Esta respuesta es elegante pero en realidad no responde la pregunta. La publicación dice que las claves que deben reemplazarse son desconocidas ... Solo sabemos que comienzan con un guión bajo, no sabemos cuáles son realmente las claves.
Dsel
2
así que esto crea un nuevo par clave / valor donde especifica la nueva clave y obtiene el valor de lo que hash.delete :old_keydevuelve y la eliminación utiliza la clave anterior. WOW, lo quiero tatuado en alguna parte :-D Gracias
Bart C
136

rails Hash tiene un método estándar para ello:

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
fuente
44
Es el método Rails, no estándar. Buena respuesta sin embargo.
user2422869
1
Además, este método no puede funcionar con claves hash de forma recursiva.
Sergio Belevskij
55
deep_transform_keys se puede usar para ello :) apidock.com/rails/v4.2.1/Hash/deep_transform_keys
gayavat
1
¡Al final! ¡Esto es exactamente lo que busqué!
TiSer
44
Esta es una parte estándar del lenguaje de Ruby 2.5: docs.ruby-lang.org/en/trunk/Hash.html#method-i-transform_keys
David Grayson
39

Si todas las teclas son cadenas y todas tienen el prefijo de subrayado, entonces puede parchear el hash en su lugar con esto:

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

El k[1, k.length - 1]bit agarra todo kexcepto el primer personaje. Si quieres una copia, entonces:

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

O

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

También puede usar subsi no le gusta la k[]notación para extraer una subcadena:

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 }

Y, si solo algunas de las teclas tienen el prefijo de subrayado:

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

Se pueden hacer modificaciones similares a todas las otras variantes anteriores, pero estas dos:

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

debería estar bien con las teclas que no tienen prefijos de subrayado sin modificaciones adicionales.

mu es demasiado corto
fuente
su respuesta funcionó, pero después de la sala encontré pocos hash como este
Manish Das
3
{"_id" => "4de7140772f8be03da000018", "_type" => "WorkStation", "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: dije "Si todas las teclas son cadenas y todas tienen el prefijo de subrayado". Incluí un enfoque de ejemplo para sus "claves sin prefijos de subrayado" en una actualización.
mu es demasiado corto el
2
@Manish: "k" es para "clave", "v" es para "valor", "x" es para "No sé cómo llamarlo, pero fui entrenado como matemático, así que lo llamo x".
mu es demasiado corto el
14

tu puedes hacer

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

¡Esto debería funcionar para su caso!

Sadiksha Gautam
fuente
11

Si queremos cambiar el nombre de una clave específica en hash, entonces podemos hacerlo de la siguiente manera:
supongamos que mi hash es my_hash = {'test' => 'ruby hash demo'}
Ahora quiero reemplazar 'prueba' por 'mensaje', entonces:
my_hash['message'] = my_hash.delete('test')

Swapnil Chincholkar
fuente
¿Cómo es su respuesta, entonces la solución para mi problema? Si pensabas que esto era útil, entonces podrías haber agregado el comentario debajo de la pregunta. Mi pregunta no era reemplazar una clave con otra clave, la solución que proporcionó es una propiedad Hash muy básica. en mi caso no es: hash[:new_key] = has[:old_key]en su lugar es: hash[:dynamic_key] = hash[:_dynamic_key]era una pregunta clara sobre expresiones regulares y no un simple reemplazo de hash.
Manish Das
2
Llegué a esto a través de una búsqueda en Google y quería la respuesta de @ Swapnil. Gracias
toobulkeh
10
h.inject({}) { |m, (k,v)| m[k.sub(/^_/,'')] = v; m }
DigitalRoss
fuente
44
Me gusta que haya intentado usar una expresión regular para filtrar los guiones bajos correctamente, pero debe tener en cuenta que en ruby, a diferencia de javascript y otros, / ^ / significa 'inicio de cadena O LÍNEA' y / $ / significa 'fin de cadena o línea '. Es poco probable que las teclas tengan nuevas líneas en este caso, pero debe tener en cuenta que el uso de esos dos operadores en ruby ​​no solo es propenso a errores, sino que también es muy peligroso cuando se usa incorrectamente en las validaciones contra inyecciones. Ver aquí para una explicación. Espero que no te importe difundir la conciencia.
Jorn van de Beek
2
hash.each {|k,v| hash.delete(k) && hash[k[1..-1]]=v if k[0,1] == '_'}
maerics
fuente
1

Fui exagerado y se me ocurrió lo siguiente. Mi motivación detrás de esto fue agregar claves de hash para evitar conflictos de alcance al fusionar / aplanar hashes.

Ejemplos

Extender clase de hash

Agrega el método rekey a las instancias de 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

Ejemplo de anteponer

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..."}

Ejemplo de recorte

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

Acoplar y anexar un "alcance"

Si devuelve un hash para volver a escribir, combinará el hash que le permite aplanar colecciones. Esto nos permite agregar alcance a nuestras claves al aplanar un hash para evitar sobrescribir una clave al fusionar.

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
fuente
0

Las respuestas anteriores son lo suficientemente buenas, pero podrían actualizar los datos originales. En caso de que no desee que se vean afectados los datos originales, puede probar mi código.

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

Primero ignorará la clave '_id' y luego se fusionará con la actualizada.

purushothaman poovai
fuente