Comment remplacer une clé de hachage par une autre clé

192

J'ai une condition où, je reçois un hash

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

et je veux ce hachage comme

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

PS : je ne sais pas quelles sont les clés dans le hachage, elles sont aléatoires qui viennent avec un préfixe "_" pour chaque clé et je ne veux pas de traits de soulignement

Manish Das
la source
Cela pourrait vous aider: stackoverflow.com/questions/4044451/…
corrodé le
+1 pour question utile
ashisrai_
@ a5his: Je suis heureux que cela ait été utile :)
Manish Das

Réponses:

711
hash[:new_key] = hash.delete :old_key
gayavat
la source
8
M'a sauvé quelques LOC, j'adore!
nicohvi
10
Je n'aime souvent pas le code ruby ​​«intelligent» car il faut un certain temps pour dire ce qu'il fait vraiment. Votre solution est en revanche simple et descriptive.
Lucas
3
Cela devrait en effet être la réponse acceptée! Facile, propre et droit au but!
GigaBass
1
Cette réponse est élégante mais ne répond pas réellement à la question. Le message indique que les clés à remplacer sont inconnues ... Nous savons seulement qu'elles commencent par un trait de soulignement, nous ne savons pas quelles sont réellement les clés.
Dsel
2
donc cela crée une nouvelle paire clé / valeur où vous spécifiez la nouvelle clé et obtenez la valeur de ce qui hash.delete :old_keyretourne et la suppression utilise l'ancienne clé. WOW, je veux qu'il soit tatoué quelque part :-D Merci
Bart C
136

rails Hash a une méthode standard pour cela:

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

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

UPD: méthode ruby ​​2.5

gayavat
la source
4
C'est la méthode Rails, pas standard. Bonne réponse cependant.
user2422869
1
En outre, cette méthode ne peut pas fonctionner avec des clés de hachage de manière récursive.
Sergio Belevskij
5
deep_transform_keys peut être utilisé pour cela :) apidock.com/rails/v4.2.1/Hash/deep_transform_keys
gayavat
1
Enfin! C'est exactement ce que j'ai cherché!
TiSer
4
Ceci est une partie standard du langage à partir de Ruby 2.5: docs.ruby-lang.org/en/trunk/Hash.html#method-i-transform_keys
David Grayson
39

Si toutes les clés sont des chaînes et qu'elles ont toutes le préfixe de soulignement, vous pouvez corriger le hachage en place avec ceci:

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

Le k[1, k.length - 1]bit saisit tout ksauf le premier caractère. Si vous voulez une copie, alors:

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 }

Vous pouvez également utiliser subsi vous n'aimez pas la k[]notation pour extraire une sous-chaîne:

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 }

Et, si seulement certaines des touches ont le préfixe de soulignement:

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

Des modifications similaires peuvent être apportées à toutes les autres variantes ci-dessus, mais ces deux:

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

devrait convenir aux clés qui n'ont pas de préfixes de soulignement sans modifications supplémentaires.

mu est trop court
la source
votre réponse a fonctionné mais après la salle, j'ai trouvé peu de hasch comme celui-ci
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: J'ai dit "Si toutes les clés sont des chaînes et qu'elles ont toutes le préfixe de soulignement". J'ai inclus un exemple d'approche pour vos "clés sans préfixes de soulignement" dans une mise à jour.
mu est trop court le
2
@Manish: "k" est pour "clé", "v" pour "valeur", "x" pour "je ne sais pas comment l'appeler mais j'ai été formé comme mathématicien donc je l'appelle x".
mu est trop court le
14

tu peux faire

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

Cela devrait fonctionner pour votre cas!

Sadiksha Gautam
la source
11

Si nous voulons renommer une clé spécifique dans le hachage, nous pouvons le faire comme suit:
Supposons que mon hachage est my_hash = {'test' => 'ruby hash demo'}
Maintenant, je veux remplacer 'test' par 'message', alors:
my_hash['message'] = my_hash.delete('test')

Swapnil Chincholkar
la source
Comment votre réponse est-elle alors la solution à mon problème? Si vous pensiez que cela était utile, vous auriez pu ajouter le commentaire sous la question. Ma question n'était pas de remplacer une clé par une autre clé, la solution que vous avez donnée est une propriété de hachage très basique. dans mon cas ce n'est pas hash[:new_key] = has[:old_key]:, c'est plutôt hash[:dynamic_key] = hash[:_dynamic_key]:, c'était une question claire sur regex et pas un simple remplacement de hachage.
Manish Das
2
J'y suis arrivé via une recherche Google et je voulais la réponse de @ Swapnil. Merci
toobulkeh
10
h.inject({}) { |m, (k,v)| m[k.sub(/^_/,'')] = v; m }
DigitalRoss
la source
4
J'aime le fait que vous ayez essayé d'utiliser une expression régulière pour filtrer correctement les traits de soulignement, mais vous devez savoir qu'en ruby, contrairement à javascript et autres, / ^ / signifie `` début de chaîne OU LIGNE '', et / $ / signifie `` fin de chaîne OU LIGNE '. Il est peu probable que les clés aient des nouvelles lignes dans ce cas, mais vous devez être conscient que l'utilisation de ces deux opérateurs dans ruby ​​est non seulement sujette aux erreurs, mais également très dangereuse lorsqu'elle est utilisée de manière incorrecte dans les validations contre les injections. Voir ici pour une explication. J'espère que cela ne vous dérange pas de répandre la conscience.
Jorn van de Beek
2
hash.each {|k,v| hash.delete(k) && hash[k[1..-1]]=v if k[0,1] == '_'}
maerics
la source
1

Je suis allé trop loin et j'ai proposé ce qui suit. Ma motivation derrière cela était d'ajouter des clés de hachage pour éviter les conflits de portée lors de la fusion / de l'aplatissement des hachages.

Exemples

Étendre la classe de hachage

Ajoute la méthode de rekey aux instances de hachage.

# 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

Exemple de pré-ajout

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

Exemple de coupe

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

Aplatir et ajouter une «portée»

Si vous renvoyez un hachage pour modifier la clé, il fusionnera le hachage, ce qui vous permet d'aplatir les collections. Cela nous permet d'ajouter de la portée à nos clés lors de l'aplatissement d'un hachage pour éviter d'écraser une clé lors de la fusion.

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
la source
0

Les réponses précédentes sont assez bonnes, mais elles peuvent mettre à jour les données d'origine. Si vous ne souhaitez pas que les données d'origine soient affectées, vous pouvez essayer mon code.

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

Il ignorera d'abord la clé '_id' puis fusionnera avec la clé mise à jour.

purushothaman poovai
la source